diff --git a/README.md b/README.md
index 41bd2191..a6af6858 100644
--- a/README.md
+++ b/README.md
@@ -1,31 +1,21 @@
-
-
- AssemblyAI CLI
-
-
+# AssemblyAI CLI
-
- Transcribe. Stream. Converse. β speech AI from your terminal.
-
+[](https://github.com/AssemblyAI/cli)
+[](https://github.com/AssemblyAI/cli/blob/main/LICENSE)
+[](https://www.assemblyai.com/docs)
-
- Quick start Β·
- Commands Β·
- Pipelines Β·
- Docs
-
+The AssemblyAI CLI (`assembly`) brings speech AI to your terminal: transcribe files, stream live audio, run a two-way voice agent, prompt the LLM Gateway, and scaffold ready-to-deploy starter apps.
-
-
-
-
-
+## π Why the AssemblyAI CLI?
----
+- **π― Everything in one command**: transcription, real-time streaming, voice agents, and LLM prompts β no SDK boilerplate.
+- **π Pipeline-friendly**: data goes to stdout, errors to stderr, `--json` for stable machine-readable output, `-` reads audio from stdin.
+- **π Secure by default**: your API key lives in the OS keyring, never in a dotfile, and run commands have no `--api-key` flag so keys can't leak into shell history.
+- **π οΈ From demo to app**: `assembly init` scaffolds a runnable FastAPI starter app, and `--show-code` prints the equivalent Python SDK script for any command.
+- **π€ Agent-ready**: `assembly setup install` wires your coding agent up with the AssemblyAI docs MCP server and skills.
+- **π Open source**: MIT licensed.
-`assembly` brings [AssemblyAI](https://www.assemblyai.com) to your terminal: transcribe files, stream live audio, run a two-way voice agent, prompt the LLM Gateway, and scaffold ready-to-deploy starter apps β all pipeline-friendly, with your key kept in the OS keyring.
-
-## Installation
+## π¦ Installation
### Homebrew (recommended β macOS / Linux)
@@ -35,7 +25,7 @@ brew trust assemblyai/cli
brew install assembly
```
-`brew install` pulls in `ffmpeg` and `portaudio` for you, so `transcribe`, `stream`, and `agent` work out of the box. Releases ship a prebuilt arm64 bottle, so `brew install`/`brew upgrade assembly` is a fast binary install (no compiler toolchain); Intel Macs build from source or can use the pipx/uv path below. Remove with `brew uninstall assembly`.
+Homebrew pulls in `ffmpeg` and `portaudio`, so `transcribe`, `stream`, and `agent` work out of the box.
### pipx / uv
@@ -47,288 +37,100 @@ pipx install "git+https://github.com/AssemblyAI/cli.git"
uv tool install "git+https://github.com/AssemblyAI/cli.git"
```
-Requires Python 3.12+. Microphone and speaker support (for `stream` and `agent`) is included by default via [`sounddevice`](https://python-sounddevice.readthedocs.io) β its macOS and Windows wheels bundle PortAudio. On Linux, install the runtime once: `sudo apt-get install libportaudio2`. You'll also want [`ffmpeg`](https://ffmpeg.org) on `PATH` to decode non-WAV/URL audio.
-
-## Quick Start
+Requires Python 3.12+. On Linux, install PortAudio once for microphone support (`sudo apt-get install libportaudio2`), and have [`ffmpeg`](https://ffmpeg.org) on `PATH` to decode non-WAV audio.
-```sh
-assembly onboard # guided setup: sign in, first transcription, start building
-```
+## π Key Features
-Prefer to do it by hand?
+- **Transcription**: `assembly transcribe` handles files, URLs, and YouTube/podcast pages, with flags for speaker labels, PII redaction, summarization, sentiment, chapters, and more.
+- **Batch transcription**: point `assembly transcribe` at a directory or glob (or pipe paths with `--from-stdin`) to transcribe everything concurrently, with sidecar files that make re-runs resumable.
+- **Real-time streaming**: `assembly stream` transcribes the microphone, a file, or a URL live β on macOS it can capture system audio too.
+- **Voice agent**: `assembly agent` runs a full-duplex spoken conversation in your terminal (use headphones).
+- **LLM Gateway**: `assembly llm` prompts an LLM over a transcript, stdin, or a live stream (`assembly stream --llm "summarize as I talk"`).
+- **Starter apps**: `assembly init` scaffolds a self-contained FastAPI + HTML app (`audio-transcription`, `live-captions`, `voice-agent`).
+- **Code generation**: add `--show-code` to `transcribe`/`stream`/`agent` to print the equivalent Python SDK script instead of running.
+- **Account self-service**: `assembly keys` / `balance` / `usage` / `limits` / `sessions` / `audit` via browser login.
-```sh
-assembly login # store your API key (browser-assisted)
-assembly transcribe --sample # transcribe the hosted wildfires.mp3 sample
-```
+## π Authentication
-## Build An App
-
-`assembly init` is how you **build a new app** β it copies a small, self-contained FastAPI + HTML project you can run locally and deploy to Vercel as-is, the starting point whenever you want to *create* something, including a voice agent app:
+### Option 1: Browser login (recommended)
```sh
-assembly init # pick a template, scaffold, install deps, open the browser
-assembly init audio-transcription myapp # non-interactive: template + directory
-assembly init voice-agent my-agent # build a voice agent app (full FastAPI + browser starter)
+assembly login
```
-Your key is written to a git-ignored `.env` (never sent to the browser). Use `--no-install` to scaffold only.
-
-> **Building a voice agent? Use `assembly init voice-agent`, not `assembly agent`.** `assembly agent` only *runs* a live mic conversation in the terminal and writes no code; `assembly init` creates the actual app.
-
-## Commands
-
-| Command | What it does |
-| --- | --- |
-| `assembly login` / `logout` / `whoami` | Manage the stored API key. |
-| `assembly doctor` | Check your environment (API key, network, ffmpeg, microphone, agent tooling). |
-| `assembly transcribe ` | Transcribe a file, URL, or YouTube/podcast page URL (`--sample`, `--llm`, `--show-code`) β or a directory/glob/stdin list as a resumable batch. |
-| `assembly transcripts list` / `get ` | Browse and fetch past transcripts. |
-| `assembly stream [file]` | Real-time transcription from a file or the microphone. |
-| `assembly agent` | *Run* a live two-way voice conversation (to **build** a voice agent app, use `assembly init voice-agent`). |
-| `assembly llm ` | Prompt the LLM Gateway (`--transcript-id`, or `--follow` for a live stream). |
-| `assembly setup install` | Set up your coding agent for AssemblyAI (docs MCP + skills). |
-| `assembly keys` / `balance` / `usage` / `limits` / `sessions` / `audit` | Account self-service (browser login). |
-
-Every command prints human-readable text by default β terminal, pipe, CI, or agent alike. Add `--json` (or `-j`) for machine-readable output; it never switches on just because stdout is piped, so `assembly transcribe call.mp3 | grep hello` still gets the transcript, not a JSON blob. Errors go to **stderr**, so stdout stays clean for pipelines.
-
-Account data lives in **top-level** commands β `assembly balance` / `usage` / `limits` / `keys` / `audit`, and `assembly login` / `logout` / `whoami` β not under an `assembly account` group.
-
-### JSON output
-
-`--json` is the scripting contract. The shapes are stable:
-
-| Command | `--json` shape |
-| --- | --- |
-| `transcribe` / `transcripts get` | the full transcript payload (`id`, `status`, `text`, `words`, `utterances`, β¦) β identical for both, so a fetched transcript round-trips |
-| `transcribe --llm` | `{id, status, text, transform: {model, steps: [{prompt, output}]}}` |
-| `transcripts list` / `sessions list` / `keys list` | a JSON array of row objects (`[]` when empty) |
-| `balance` / `usage` / `limits` / `audit` | the raw AMS payload (e.g. `balance.balance_in_cents`; `usage.usage_items[].line_items[].price` in cents) |
-| `doctor` | `{ok, profile, environment, checks: [{name, status, affects, detail, fix}]}` |
-| any error | `{"error": {"type", "message", "suggestion"?, "transcript_id"?}}` on **stderr** |
-
-`stream`/`agent` with `--json` emit newline-delimited JSON (one object per event/turn).
-
-### Exit codes
-
-Scripts can branch on the exit code:
-
-| Code | Meaning |
-| --- | --- |
-| `0` | success |
-| `1` | API/network error, missing dependency, or unexpected internal error |
-| `2` | usage/validation error (bad flag, bad path, malformed id, unusable config) |
-| `4` | not authenticated (no usable key, rejected key, or a self-service command needing browser login) |
-| `130` | cancelled with Ctrl-C |
-
-`assembly deploy` / `assembly dev` shell out to other tools and propagate that tool's own exit code.
-
-> **Tip:** Quote URLs that contain `?` (most YouTube links do) β in zsh the `?` is a glob character: `assembly transcribe "https://www.youtube.com/watch?v=VIDEO_ID"`.
+Stores your API key in the OS keyring (Keychain / Credential Manager / Secret Service).
-## Transcribe A File
-
-`assembly transcribe` exposes the full `TranscriptionConfig` surface as curated, grouped flags β model & language, formatting, speakers & channels, PII/safety guardrails, analysis (summary, chapters, sentiment, entities, topics, highlights), customization, and webhooks:
-
-```sh
-assembly transcribe call.mp3 \
- --speaker-labels --speakers-expected 2 \
- --redact-pii --redact-pii-policy person_name,phone_number \
- --summarization --summary-type bullets \
- --sentiment-analysis --auto-chapters \
- --config speech_threshold=0.5 \
- --config-file extra.json
-```
-
-Anything without a curated flag is reachable via the escape hatch: `--config KEY=VALUE` (repeatable) and `--config-file FILE` (a JSON object) accept any SDK field by name. Precedence: config file < `--config` < explicit flags. Run `assembly transcribe --help` for the full flag list.
-
-## Stream Live Audio
-
-```sh
-assembly stream --sample # stream the hosted wildfires.mp3 sample
-assembly stream path/to/audio.wav # 16 kHz mono WAV streams directly (other formats need ffmpeg)
-assembly stream https://β¦/clip.mp3 # a URL works too (decoded via ffmpeg)
-assembly stream # from the microphone; Ctrl-C to stop
-assembly stream --system-audio # macOS: system/app audio + mic as separate sessions
-assembly stream --system-audio-only # macOS: system/app audio without the mic
-```
-
-`assembly stream` exposes the full `StreamingParameters` surface (model & input, turn detection, features) as curated flags, with the same `--config` / `--config-file` escape hatch:
+### Option 2: Environment variable
```sh
-assembly stream --sample --max-turn-silence 400 --format-turns \
- --keyterms-prompt "AssemblyAI" --config vad_threshold=0.7
+export ASSEMBLYAI_API_KEY="YOUR_API_KEY"
```
-On macOS, `--system-audio` uses ScreenCaptureKit to capture system/app audio without a loopback driver and labels finalized turns `You:` or `System:`. The first run may prompt for Screen & System Audio Recording and Microphone permissions.
+Checked before the keyring, so nothing is written to disk β ideal for CI (set it as a masked secret).
-## Live Transcript β Live LLM
+## π Getting Started
-Run a prompt over the live transcript through the LLM Gateway, refreshing on every finalized turn β one command, no pipe to wire up:
+### Basic usage
```sh
-assembly stream --llm "summarize action items as I talk"
-assembly stream --llm "extract action items" --llm "rewrite them as a checklist" # chains
-```
+# Guided setup: sign in, first transcription, start building
+assembly onboard
-On a terminal you watch one evolving panel; add `--json` for one JSON object per refresh. Prefer the pipe? Compose the primitives β `assembly stream -o text` writes one finalized turn per line and `assembly llm -f` re-runs your prompt over the growing transcript:
+# Transcribe the hosted sample, then your own audio
+assembly transcribe --sample
+assembly transcribe call.mp3
-```sh
-assembly stream -o text | assembly llm -f --system "You are a meeting scribe" "summarize action items"
-```
+# Stream the microphone live (Ctrl-C to stop)
+assembly stream
-## Voice Agent
+# Talk to a voice agent
+assembly agent
-Have a live, two-way voice conversation β full-duplex, so you can interrupt mid-sentence (barge-in). **Use headphones**, otherwise the agent hears itself. (This only *runs* a conversation; to **build** a voice-agent app, use `assembly init voice-agent`.)
-
-```sh
-assembly agent # talk; the agent talks back. Ctrl-C to stop.
-assembly agent --voice james --greeting "Hi"
-assembly agent --system-prompt-file persona.txt # load the system prompt from a file
-assembly agent --list-voices # see available voices
+# Scaffold a starter app
+assembly init
```
-## Show The Code
-
-Add `--show-code` to `transcribe`, `stream`, or `agent` to print the equivalent Python SDK script **instead of running** β a ready-to-edit starting point built from exactly the flags you passed. It needs no API key (generated code reads `ASSEMBLYAI_API_KEY`) and writes plain Python to stdout:
+### Quick examples
```sh
-assembly transcribe --sample --speaker-labels --show-code # print the equivalent script
-assembly transcribe call.mp3 --sentiment-analysis --show-code > my_transcribe.py
-assembly stream --show-code # the microphone-streaming idiom
-assembly agent --voice ivy --show-code # the full-duplex agent loop
-```
-
-With `--llm` (repeatable), it emits the chained LLM Gateway calls too.
-
-## Pipelines
-
-`assembly` composes with the rest of your shell. Output is machine-clean (errors β stderr), commands read `-` from stdin, and `-o`/`--output` prints a single field so you rarely need `jq`.
+# Just the text, or captions
+assembly transcribe call.mp3 -o text
+assembly transcribe video.mp4 -o srt
-```sh
-# Pick one field with -o
-assembly transcribe call.mp3 -o text # just the transcript text
-assembly transcribe video.mp4 -o srt # SubRip (.srt) captions
-assembly transcribe call.mp3 --json | jq . # full JSON when you do want jq
+# Speaker labels + summary, as JSON
+assembly transcribe call.mp3 --speaker-labels --summarization --json
-# Read audio from stdin
-ffmpeg -i talk.mp4 -f wav - | assembly transcribe - # transcribe any video
-curl -sL https://example.com/ep.mp3 | assembly transcribe - # no temp file
+# Batch: a whole directory or glob, resumable on re-run
+assembly transcribe ./recordings
+find . -name "*.wav" | assembly transcribe --from-stdin
-# assembly llm is a general text filter β it reads stdin, audio optional
+# Pipe audio in, pipe text out
+ffmpeg -i talk.mp4 -f wav - | assembly transcribe -
git log --oneline -30 | assembly llm "write release notes grouped by feature/fix"
-# DIY voice assistant β speak a question, hear the answer (use headphones)
-assembly stream -o text | while IFS= read -r line; do
- echo "$line" | assembly llm -o text "answer in one short sentence" | say
-done
+# Print the equivalent Python SDK script instead of running
+assembly transcribe --sample --speaker-labels --show-code
```
-A Ctrl-C in a pipe hits both sides; to stop just the producer and let the consumer finish, signal the producer (`timeout -s INT 30s assembly stream β¦`) or end on a natural pause (`assembly stream --inactivity-timeout 5`).
-
-## Batch transcription
-
-Point `assembly transcribe` at a directory or glob β or pipe a list of paths/URLs with `--from-stdin` β and it transcribes everything concurrently behind a live progress table:
-
-```sh
-assembly transcribe ./recordings # every audio file under the directory
-assembly transcribe "calls/*.mp3" # glob (quote it so your shell doesn't expand it)
-find . -name "*.wav" | assembly transcribe --from-stdin # composes with find/ls/yt-dlp
-assembly transcribe ./recordings --concurrency 8 # default is 4 at a time
-```
-
-Each source gets a `.aai.json` sidecar with the full transcript payload. The sidecar is also the resume marker: a re-run skips any source whose sidecar records a completed transcription of the same bytes (hash-checked for local files), so retrying a partly-failed batch only pays for what's missing. `--force` re-transcribes everything. Under `--json`, batch mode emits one NDJSON record per source as it finishes (`{source, status, id, sidecar}`, or `{source, status, error}` on failure); the exit code is `1` if any source failed.
-
-## API Key & Security
-
-`assembly` resolves your key in order: the `ASSEMBLYAI_API_KEY` environment variable, then the OS keyring (written only by `assembly login`). Two things worth knowing:
-
-- The key is **never stored in a plaintext dotfile** β `assembly login` puts it in the OS keyring (Keychain / Credential Manager / Secret Service); the only on-disk config holds just profile names.
-- There is **no `--api-key` flag on run commands**, so a key can't leak into `ps` output or shell history.
-
-Prefer not to persist it? Set the env var instead β it's checked *before* the keyring, so nothing is written to disk. Scope it to one command (and keep it out of history) by injecting from a secret manager at call time:
-
-```sh
-ASSEMBLYAI_API_KEY=$(op read "op://Private/AssemblyAI/api key") assembly transcribe call.mp3
-op run -- assembly transcribe call.mp3 # β¦or wrap the whole command
-```
-
-In CI, set `ASSEMBLYAI_API_KEY` as a masked secret. `assembly logout` purges the keyring entry; `assembly whoami` / `assembly doctor` confirm the active source without printing the key.
-
-## Telemetry
-
-`assembly` collects **anonymous** usage telemetry to help improve the CLI: the command name (never its arguments), outcome class and exit code, duration, CLI version, OS, Python version, whether it ran in CI, and a random install id. It never collects arguments, file paths or contents, transcripts, API keys, or account data β and delivery runs in a detached background process, so it never slows a command down.
-
-Opt out any time, persistently or per-environment:
-
-```sh
-assembly telemetry disable # persisted on this machine (assembly telemetry status to inspect)
-export AAI_TELEMETRY_DISABLED=1 # env kill-switch; the cross-tool DO_NOT_TRACK=1 also works
-```
-
-The ingestion credential in the source is a Datadog **client token** β the write-only, embeddable credential class (it can submit events, read nothing). No account secret ships with the CLI.
-
-## Account Self-Service
-
-These commands use your browser login session (run `assembly login`), not your API key:
-
-```sh
-assembly keys list # list API keys (masked) across projects
-assembly keys create --name ci-pipeline # mint a new key (printed once)
-assembly balance # remaining account balance
-assembly usage --start 2026-05-01 --end 2026-06-01
-assembly sessions list --status completed
-assembly audit --action token.create # account audit log, filterable
-```
-
-AMS sessions are short-lived β if a command reports it needs a browser login, run `assembly login` again.
-
-## AI Coding Agents
-
-Set your coding agent up for AssemblyAI β the live docs (MCP server), the AssemblyAI skill, and the bundled `aai-cli` skill β so your agent writes current, correct integration code:
-
-```sh
-assembly setup install # docs MCP + assemblyai skill + bundled aai-cli skill (user scope)
-assembly setup status # show what's set up
-assembly setup remove # unwind all three
-```
-
-`install` shells out to `claude mcp add` for the MCP and `npx skills add` for the `assemblyai` skill; the `aai-cli` skill ships inside the package and is copied in directly (no network). Pass `--scope project` to scope the MCP server to the current project. A missing `claude` or `npx` is reported and skipped, not treated as an error.
-
-## Reference
-
-Use `--help` on any command to explore flags and examples:
-
-```sh
-assembly --help
-assembly transcribe --help
-assembly stream --help
-```
+## π Documentation
+- Run `assembly --help` or `assembly --help` for flags and examples.
+- Run `assembly doctor` to check your environment (API key, network, ffmpeg, microphone).
- [AssemblyAI docs](https://www.assemblyai.com/docs)
- [API reference](https://www.assemblyai.com/docs/api-reference)
-## Development
+## π€ Contributing
-This project uses [uv](https://docs.astral.sh/uv/). Run tools through `uv run` so they use the locked environment (`pyproject.toml` + `uv.lock`):
+This project uses [uv](https://docs.astral.sh/uv/):
```sh
-uv sync # create/refresh the venv (the dev group installs by default)
-uv run assembly --help # run the CLI from the locked environment
-uv run pytest # run the test suite (uv run mypy / ruff likewise)
-./scripts/check.sh # ruff + mypy + pytest β the same checks CI runs on every PR
+uv sync # create/refresh the venv
+uv run assembly --help # run the CLI from the locked environment
+./scripts/check.sh # the full gate CI runs
```
-### Releasing
-
-Releases ship as a prebuilt arm64 Homebrew bottle, so `brew install`/`brew upgrade assembly` is a fast binary install. To cut one:
-
-1. Bump `version` in `pyproject.toml` (keep `aai_cli/__init__.py` `__version__` in sync) and merge that PR.
-2. From an up-to-date `main`, run `./scripts/cut_release.sh` β it checks the tree is clean, on `main`, and in sync with origin, then tags `vX.Y.Z` and pushes it. (`--dry-run` verifies without tagging.)
-3. The tag triggers [`.github/workflows/release.yml`](.github/workflows/release.yml): it builds the bottle, creates the GitHub Release, and opens a `release/vX.Y.Z-formula` PR pinning the formula and adding the bottle block.
-4. Merge that formula PR with the repo-admin "merge without waiting for requirements" override β a `GITHUB_TOKEN`-opened PR doesn't trigger CI, so the required check won't report on its own.
+See [AGENTS.md](AGENTS.md) for development conventions and architecture notes.
-## License
+## π Legal
Released under the [MIT license](LICENSE).
-
-