╭─────────┬───────┬───────────────┬────────┬──────┬────────────┬──────────────────────────────────────────╮
+│LEVEL│ALIAS│SHORT ID│COUNT│SEEN│FIXABILITY│TITLE│
+├─────────┼───────┼───────────────┼────────┼──────┼────────────┼──────────────────────────────────────────┤
+│ERROR│f-79│FRONTEND-79│ 2.4k │ 2h │92%│TypeError: Cannot read property 'map'...│
+│WARN│a-2f│API-2F│ 891 │ 1d │67%│ReferenceError: user is not defined│
+│ERROR│m-4d│MOBILE-4D│ 456 │ 5d │23%│NetworkError: Failed to fetch│
+╰─────────┴───────┴───────────────┴────────┴──────┴────────────┴──────────────────────────────────────────╯
+
+
+
+
+ $
+ sentry issue explain f-79
+
+
+
Root Cause Analysis Complete
+
══════════════════════════════
+
+
+ Cause:
+ The userData response from /api/users returns
+
+
+ null when user session expires, but the component
+
+
+ assumes it's always an array and calls .map() directly.
+
+
+
+ →
+ Add null check before mapping: userData?.map()
+
+
+
+
+
+
diff --git a/apps/cli-docs/src/content.config.ts b/apps/cli-docs/src/content.config.ts
new file mode 100644
index 000000000..7fbcf2c33
--- /dev/null
+++ b/apps/cli-docs/src/content.config.ts
@@ -0,0 +1,7 @@
+import { defineCollection } from "astro:content";
+import { docsLoader } from "@astrojs/starlight/loaders";
+import { docsSchema } from "@astrojs/starlight/schema";
+
+export const collections = {
+ docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
+};
diff --git a/apps/cli-docs/src/content/docs/agent-guidance.md b/apps/cli-docs/src/content/docs/agent-guidance.md
new file mode 100644
index 000000000..d9b4eb20e
--- /dev/null
+++ b/apps/cli-docs/src/content/docs/agent-guidance.md
@@ -0,0 +1,335 @@
+---
+title: Agent Guidance
+description: Operational guidance for AI coding agents using the Sentry CLI
+---
+
+Best practices and operational guidance for AI coding agents using the Sentry CLI.
+
+## Key Principles
+
+- **Just run the command** — the CLI handles authentication and org/project detection automatically. Don't pre-authenticate or look up org/project before running commands. The CLI prompts for login if needed.
+- **Prefer CLI commands over raw API calls** — the CLI has dedicated commands for most tasks. Reach for `sentry issue view`, `sentry issue list`, `sentry trace view`, etc. before constructing API calls manually or fetching external documentation.
+- **Use `sentry docs` for setup questions** — if you need to know how to configure a Sentry SDK or feature, run `sentry docs "your question"` to query the documentation directly. This is faster and more accurate than fetching docs externally.
+- **Use `sentry schema` to explore the API** — if you need to discover API endpoints, run `sentry schema` to browse interactively or `sentry schema ` to search. This is faster than fetching OpenAPI specs externally.
+- **Use `sentry issue view` to investigate issues** — when asked about a specific issue (e.g., `CLI-G5`, `PROJECT-123`), use `sentry issue view` directly. Multiple IDs can be passed in one invocation: `sentry issue view A B C --json` returns an array of the same objects.
+- **Use `--json` for machine-readable output** — pipe through `jq` for filtering. Human-readable output includes formatting that is hard to parse.
+- **The CLI auto-detects org/project — don't discover it yourself** — most commands work without explicit targets by checking `.sentryclirc` config files, scanning for DSNs in `.env` files and source code, and matching directory names. Do **not** run `sentry org list` and then `sentry project list` to figure out which project this checkout belongs to — that manual fan-out just replicates the detection the CLI already runs on every command. Only specify `/` when the CLI reports it can't detect the target or detects the wrong one.
+
+## Design Principles
+
+The `sentry` CLI follows conventions from well-known tools — if you're familiar with them, that knowledge transfers directly:
+
+- **`gh` (GitHub CLI) conventions**: The `sentry` CLI uses the same `` command pattern (e.g., `sentry issue list`, `sentry org view`). Flags follow `gh` conventions: `--json` for machine-readable output, `--fields` to select specific fields, `-w`/`--web` to open in browser, `-q`/`--query` for filtering, `-n`/`--limit` for result count.
+- **`sentry api` mimics `curl`**: The `sentry api` command provides direct API access with a `curl`-like interface — `--method` for HTTP method, `--data` for request body, `--header` for custom headers. It handles authentication automatically. If you know how to call a REST API with `curl`, the same patterns apply.
+
+## Context Window Tips
+
+- Use `--json --fields` to select specific fields and reduce output size. Run ` --help` to see available fields. Example: `sentry issue list --json --fields shortId,title,priority,level,status`
+- Use `--json` when piping output between commands or processing programmatically
+- Use `--limit` to cap the number of results (default is usually 10–100)
+- Prefer `sentry issue view PROJECT-123` over listing and filtering manually
+- Pass multiple issue IDs in one call (`sentry issue view A B C --json`) instead of looping `issue view` per ID
+- Analyze multiple issues in one call (`sentry issue explain A B C --json`) instead of looping `issue explain` per ID
+- Use `sentry api` for endpoints not covered by dedicated commands
+
+## Safety Rules
+
+- Always confirm with the user before running destructive commands: `project delete`, `trial start`
+- For mutations, verify the org/project context looks correct in the command output before proceeding with further changes
+- Never store or log authentication tokens — the CLI manages credentials automatically
+- If the CLI reports the wrong org/project, override with explicit `/` arguments
+
+## Exit Codes
+
+The CLI uses semantic exit codes. Key ranges for agents:
+
+| Range | Meaning | Agent Action |
+|-------|---------|-------------|
+| 0 | Success | Proceed normally |
+| 10–19 | Auth error | Prompt user to run `sentry auth login` |
+| 20–29 | Input error | Check command arguments and retry |
+| 30–39 | API error | Retry or report to user |
+| 40–49 | Feature unavailable | Inform user about plan/settings |
+| 50–59 | Operation error | Report to user |
+| 60–69 | Command-specific | Check stderr for details |
+
+See [Exit Codes](/exit-codes/) for the complete reference.
+
+## Workflow Patterns
+
+### Investigate an Issue
+
+```bash
+# 1. Find the issue (auto-detects org/project from DSN or config)
+sentry issue list --query "is:unresolved" --limit 5
+
+# 2. Get details. For agents, prefer --json — it includes the full issue plus
+# the latest event under `event`, so you get everything in one call.
+sentry issue view PROJECT-123 --json
+
+# 3. Get AI root cause analysis
+sentry issue explain PROJECT-123
+
+# 4. Get a fix plan
+sentry issue plan PROJECT-123
+```
+
+`sentry issue view --json` is the fastest way to get an agent up to
+speed on an issue. Select just the fields you need with `--fields` instead of
+consuming the whole payload — the latest event's `request` entry can carry live
+session data (cookies, headers, body), so extract named fields rather than
+dumping the entire object:
+
+```bash
+# Top-level issue fields
+sentry issue view PROJECT-123 --json --fields shortId,title,culprit,count,userCount,permalink
+
+# Named fields from the latest event — avoids pulling the full request/session blob
+sentry issue view PROJECT-123 --json --fields event.id,event.title,event.dateCreated
+
+# Just the request URL and method (not the whole request entry). Event data
+# lives under event.entries[], each tagged with a `type` and `data` payload.
+sentry issue view PROJECT-123 --json | jq '.event.entries[] | select(.type == "request") | .data | {url, method}'
+```
+
+### Explore Traces and Performance
+
+```bash
+# 1. List recent traces (auto-detects org/project)
+sentry trace list --limit 5
+
+# 2. View a specific trace with span tree
+sentry trace view abc123def456...
+
+# 3. View spans for a trace
+sentry span list abc123def456...
+
+# 4. View logs associated with a trace
+sentry trace logs abc123def456...
+```
+
+### Stream Logs
+
+```bash
+# Stream logs in real-time (auto-detects org/project)
+sentry log list --follow
+
+# Filter logs by severity
+sentry log list --query "severity:error"
+```
+
+### Capture Events Locally (Spotlight)
+
+```bash
+# Run the app with the local server auto-enabled; tail errors/traces/logs.
+# No DSN needed — with no DSN, events go ONLY to the local server (nothing
+# reaches the user's Sentry org, no production quota). With a DSN set, the
+# SDK sends to both.
+sentry local run -- npm run dev # or: python manage.py runserver, etc.
+
+# From a CLI source checkout, run the Local UI in a second terminal, then open it.
+pnpm --filter local dev
+sentry local run --open -- npm run dev
+
+# Watch only AI/agent (gen_ai, mcp) spans while iterating on an agent.
+sentry local -f ai
+
+# Server-side SDKs read SENTRY_SPOTLIGHT automatically. The CLI also injects
+# the URL under every framework client prefix (NEXT_PUBLIC_, VITE_, PUBLIC_,
+# NUXT_PUBLIC_, REACT_APP_, VUE_APP_, GATSBY_). Until the browser SDK reads
+# these automatically (getsentry/sentry-javascript#18198), reference the var
+# matching your framework in the client config:
+# Sentry.init({ spotlight: process.env.NEXT_PUBLIC_SENTRY_SPOTLIGHT ?? false })
+```
+
+### Query Sentry Documentation
+
+```bash
+# Ask a documentation question
+sentry docs "How do I configure tracing in Next.js?"
+
+# Search the documentation index
+sentry docs list "source maps"
+```
+
+### Check Sentry Service Status
+
+```bash
+# Show current status of Sentry services
+sentry status
+
+# Machine-readable status
+sentry status --json
+```
+
+### Explore the API Schema
+
+```bash
+# Browse all API resource categories
+sentry schema
+
+# Search for endpoints related to a resource
+sentry schema issues
+
+# Get details about a specific endpoint
+sentry schema "GET /api/0/organizations/{organization_id_or_slug}/issues/"
+```
+
+### Manage Releases
+
+```bash
+# Create a release — version must match Sentry.init({ release }) exactly
+sentry release create my-org/1.0.0 --project my-project
+
+# Associate commits via repository integration (needs local git checkout)
+sentry release set-commits my-org/1.0.0 --auto
+
+# Or read commits from local git history (no integration needed)
+sentry release set-commits my-org/1.0.0 --local
+
+# Mark the release as finalized
+sentry release finalize my-org/1.0.0
+
+# Record a production deploy
+sentry release deploy my-org/1.0.0 production
+```
+
+**Key details:**
+- The positional is `/`. In `sentry release create sentry/1.0.0`, `sentry` is the org and `1.0.0` is the version — the slash separates org from version, it is not part of the version string.
+- The **version** must match the `release` value in `Sentry.init()`. If your SDK uses `"1.0.0"`, the command must use `org/1.0.0`.
+- `--auto` requires a Sentry repository integration (GitHub/GitLab/Bitbucket) **and** a local git checkout. It matches your `origin` remote against Sentry's repo list. Without a checkout, use `--local`.
+- With no flag, `set-commits` tries `--auto` first and falls back to `--local` on failure.
+
+### View Agent Conversations
+
+```bash
+# List recent agent conversations (org auto-detected)
+sentry agent-conversation list
+
+# Explicit org, last 24 hours
+sentry agent-conversation list my-org --period 24h
+
+# View a conversation transcript
+sentry agent-conversation view my-org/conv-123
+
+# JSON output for programmatic access
+sentry agent-conversation view conv-123 --json
+```
+
+### Process WebAssembly Modules
+
+```bash
+# Add a build id to a wasm module (no auth required)
+sentry wasm-split app.wasm
+
+# Capture the build id for a later debug-files upload
+BUILD_ID=$(sentry wasm-split app.wasm)
+
+# Split debug data into a companion and strip the shipped binary
+sentry wasm-split app.wasm --debug-out app.debug.wasm --strip
+```
+
+### Arbitrary API Access
+
+```bash
+# GET request (default)
+sentry api /api/0/organizations/my-org/
+
+# POST request with data
+sentry api /api/0/organizations/my-org/projects/ --method POST --data '{"name":"new-project","platform":"python"}'
+```
+
+## Dashboard Layout
+
+Sentry dashboards use a **6-column grid**. When adding widgets, aim to fill complete rows (widths should sum to 6).
+
+Display types with default sizes:
+
+| Display Type | Width | Height | Category | Notes |
+|---|---|---|---|---|
+| `big_number` | 2 | 1 | common | Compact KPI — place 3 per row (2+2+2=6) |
+| `line` | 3 | 2 | common | Half-width chart — place 2 per row (3+3=6) |
+| `area` | 3 | 2 | common | Half-width chart — place 2 per row |
+| `bar` | 3 | 2 | common | Half-width chart — place 2 per row |
+| `table` | 6 | 2 | common | Full-width — always takes its own row |
+| `stacked_area` | 3 | 2 | specialized | Stacked area chart |
+| `top_n` | 3 | 2 | specialized | Top N ranked list |
+| `categorical_bar` | 3 | 2 | specialized | Categorical bar chart |
+| `text` | 3 | 2 | specialized | Static text/markdown widget |
+| `details` | 3 | 2 | internal | Detail view |
+| `wheel` | 3 | 2 | internal | Pie/wheel chart |
+| `rage_and_dead_clicks` | 3 | 2 | internal | Rage/dead click visualization |
+| `server_tree` | 3 | 2 | internal | Hierarchical tree display |
+| `agents_traces_table` | 3 | 2 | internal | Agents traces table |
+
+Use **common** types for general dashboards. Use **specialized** only when specifically requested. Avoid **internal** types unless the user explicitly asks.
+
+Available datasets: `spans` (default), `errors`, `metrics`, `issue`, `logs`. Run `sentry dashboard widget --help` for dataset descriptions, query formats, and examples.
+
+**Row-filling examples:**
+
+```bash
+# 3 KPIs filling one row (2+2+2 = 6)
+sentry dashboard widget add "Error Count" --display big_number --query count
+sentry dashboard widget add "P95 Duration" --display big_number --query p95:span.duration
+sentry dashboard widget add "Throughput" --display big_number --query epm
+
+# 2 charts filling one row (3+3 = 6)
+sentry dashboard widget add "Errors Over Time" --display line --query count
+sentry dashboard widget add "Latency Over Time" --display line --query p95:span.duration
+
+# Full-width table (6 = 6)
+sentry dashboard widget add "Top Endpoints" --display table \
+ --query count --query p95:span.duration \
+ --group-by transaction --sort -count --limit 10
+```
+
+## Quick Reference
+
+### Time filtering
+
+Use `--period` (alias: `-t`) to filter by time window:
+
+```bash
+sentry trace list --period 1h
+sentry span list --period 24h
+sentry span list -t 7d
+```
+
+### Scoping to an org or project
+
+Org and project are positional arguments following `gh` CLI conventions:
+
+```bash
+sentry trace list my-org/my-project
+sentry issue list my-org/my-project
+sentry span list my-org/my-project/abc123def456...
+```
+
+### Listing spans in a trace
+
+Pass the trace ID as a positional argument to `span list`:
+
+```bash
+sentry span list abc123def456...
+sentry span list my-org/my-project/abc123def456...
+```
+
+### Dataset names for the Events API
+
+When querying the Events API (directly or via `sentry api`), valid dataset values are: `spans`, `logs`, `errors`, `tracemetrics`, `profile_functions`, and `uptime_results`.
+
+## Common Mistakes
+
+- **Wrong issue ID format**: Use `PROJECT-123` (short ID), not the numeric ID `123456789`. The short ID includes the project prefix.
+- **Pre-authenticating unnecessarily**: Don't run `sentry auth login` before every command. The CLI detects missing/expired auth and prompts automatically. Only run `sentry auth login` if you need to switch accounts.
+- **Missing `--json` for piping**: Human-readable output includes formatting. Use `--json` when parsing output programmatically.
+- **Specifying org/project when not needed**: Auto-detection resolves org/project from `.sentryclirc` config files, DSNs, env vars, and directory names. Let it work first — only add `/` if the CLI says it can't detect the target or detects the wrong one.
+- **Manually discovering the project before running a command**: Don't list the orgs you belong to, then list every project in each, to match the local checkout to a project — the CLI already does exactly this resolution internally on each command. Skip the fan-out and run the command directly; correct the target afterwards only if the output shows the wrong org/project.
+- **Confusing `--query` syntax**: The `--query` flag uses Sentry search syntax (e.g., `is:unresolved`, `assigned:me`), not free text search.
+- **Not using `--web`**: View commands support `-w`/`--web` to open the resource in the browser — useful for sharing links.
+- **Fetching API schemas instead of using the CLI**: Prefer `sentry schema` to browse the API and `sentry api` to make requests — the CLI handles authentication and endpoint resolution, so there's rarely a need to download OpenAPI specs separately.
+- **Fetching Sentry docs externally**: Use `sentry docs "your question"` to query Sentry's documentation from the CLI — this returns concise answers with source links, without needing to fetch or parse documentation pages.
+- **Release version mismatch**: The `org/version` positional is `/`, where `org/` is the org, not part of the version. `sentry release create sentry/1.0.0` creates version `1.0.0` in org `sentry`. If your `Sentry.init()` uses `release: "1.0.0"`, this is correct. Don't double-prefix like `sentry/myapp/1.0.0`.
+- **Running `set-commits --auto` without a git checkout**: `--auto` needs a local git repo to discover the origin remote URL and HEAD commit. In CI, ensure `actions/checkout` with `fetch-depth: 0` runs before `set-commits --auto`.
+- **Using `sentry api` when CLI commands suffice**: `sentry issue list --json` and `sentry issue view --json` already include `shortId`, `title`, `count`, `userCount`, `priority`, `level`, `status`, `permalink`, and other fields at the top level. When using `--fields` to select specific fields like `count` or `userCount`, the CLI automatically ensures these fields are present in the API response. Use `--fields` to select specific fields and `--help` to see all available fields. Only fall back to `sentry api` for data the CLI doesn't expose.
diff --git a/apps/cli-docs/src/content/docs/agentic-usage.md b/apps/cli-docs/src/content/docs/agentic-usage.md
new file mode 100644
index 000000000..8caf49980
--- /dev/null
+++ b/apps/cli-docs/src/content/docs/agentic-usage.md
@@ -0,0 +1,82 @@
+---
+title: Agentic Usage
+description: Enable AI coding agents to use the Sentry CLI
+---
+
+AI coding agents can use the Sentry CLI through the skill system. The CLI detects and supports Claude Code (including Cowork), Cursor, Windsurf, GitHub Copilot, Gemini CLI, OpenAI Codex, Goose, Amp, Augment, OpenCode, Cline, Grok, Kimi, Junie, OpenClaw, and any agent that reads skills from `~/.agents`. This allows agents to interact with Sentry directly from your development environment.
+
+## Automatic Installation
+
+The curl installer and Homebrew run `sentry cli setup`, which installs agent skills into detected agent root directories (`~/.claude`, `~/.agents`). After a package-manager install, run `sentry cli setup` yourself. `sentry cli upgrade` also refreshes skills. No network fetch is needed — skill files are embedded in the binary.
+
+This uses the same `~/.agents` convention as [dotagents](https://github.com/getsentry/dotagents), Sentry's first-party tool for installing agent skills. See [Manual Installation](#manual-installation) to add the skill with dotagents yourself.
+
+To skip automatic skill installation, pass `--no-agent-skills` to `sentry cli setup`.
+
+## Manual Installation
+
+### dotagents (recommended)
+
+[dotagents](https://github.com/getsentry/dotagents) is Sentry's first-party tool for installing agent skills. It configures Claude, Cursor, Codex, Grok, VS Code, OpenCode, and more from a single `agents.toml`.
+
+Add the Sentry CLI skill from its well-known source:
+
+```bash
+npx @sentry/dotagents add https://cli.sentry.dev sentry-cli
+```
+
+This installs the skill globally under `~/.agents/` and makes it available across all your projects. The first `add` bootstraps `~/.agents/agents.toml` for you — no `init` step is required.
+
+To install the skill for a single repository instead, initialize project scope first, then add it:
+
+```bash
+npx @sentry/dotagents --project init
+npx @sentry/dotagents --project add https://cli.sentry.dev sentry-cli
+```
+
+### skills
+
+Alternatively, use the `skills` CLI:
+
+```bash
+npx skills add https://cli.sentry.dev
+```
+
+Either command registers the Sentry CLI as a skill that your agent can invoke when needed.
+
+## Capabilities
+
+With this skill, agents can:
+
+- **View issues** - List and inspect Sentry issues from your projects
+- **Inspect events** - Look at specific error events and their details
+- **AI analysis** - Get root cause analysis and fix plans via Seer AI
+- **Browse projects** - List projects and organizations you have access to
+- **Explore the API** - Browse API endpoints with `sentry schema` and make arbitrary requests with `sentry api`
+- **Query documentation** - Ask questions about Sentry setup and configuration with `sentry docs`
+- **Make API calls** - Execute arbitrary Sentry API requests
+- **Authenticate** - Help you set up CLI authentication
+- **View agent conversations** - List and inspect AI agent conversation transcripts with `sentry agent-conversation`
+- **Check service status** - Query the Sentry status page with `sentry status` (no auth required)
+- **Process WebAssembly** - Add build IDs and split debug data from wasm modules with `sentry wasm-split` (no auth required)
+
+## How It Works
+
+When you ask your agent about Sentry errors or want to investigate an issue, the agent uses CLI commands to fetch real data from your Sentry account. For example:
+
+- "Show me the latest issues in my project" → `sentry issue list`
+- "What's the stack trace for ISSUE-123?" → `sentry issue view ISSUE-123`
+- "List all projects in my organization" → `sentry project list my-org`
+- "What API endpoints exist for releases?" → `sentry schema releases`
+- "How do I set up source maps for Next.js?" → `sentry docs "source maps Next.js"`
+- "What is Sentry's status right now?" → `sentry status`
+- "Show me recent agent conversations" → `sentry agent-conversation list`
+
+The CLI has dedicated commands for most Sentry tasks, so agents should prefer `sentry` commands over constructing raw API calls. The `sentry docs` command queries Sentry's documentation directly from the terminal, the `sentry schema` command provides built-in API exploration, and `sentry api` handles authenticated requests for anything not covered by a dedicated command.
+
+The skill uses your existing CLI authentication, so you'll need to run `sentry auth login` first if you haven't already.
+
+## Requirements
+
+- An authenticated Sentry CLI installation (`sentry auth login`)
+- An AI coding agent that supports the skills system (e.g., Claude Code, Cursor, Windsurf, GitHub Copilot, Gemini CLI, Codex, Goose, Amp, Augment, OpenCode, Cline, Grok, Kimi, Junie, OpenClaw, or any agent that reads from `~/.agents`)
diff --git a/apps/cli-docs/src/content/docs/contributing.md b/apps/cli-docs/src/content/docs/contributing.md
new file mode 100644
index 000000000..3e932f7cc
--- /dev/null
+++ b/apps/cli-docs/src/content/docs/contributing.md
@@ -0,0 +1,171 @@
+---
+title: Contributing
+description: How to contribute to the Sentry CLI
+---
+
+We welcome contributions to the Sentry CLI! This guide will help you get started.
+
+## Development Setup
+
+### Prerequisites
+
+
+- [Node.js](https://nodejs.org) (v22.15 or later)
+- [pnpm](https://pnpm.io) (v10.11 or later)
+
+- Git
+
+### Getting Started
+
+```bash
+# Clone the repository
+git clone https://github.com/getsentry/cli.git
+cd cli
+
+# Install dependencies
+pnpm install
+
+# Run CLI in development mode
+pnpm run cli -- --help
+
+# Run tests
+pnpm run test
+```
+
+### Environment Variables
+
+Create a `.env.local` file for development:
+
+```bash
+cp .env.example .env.local
+```
+
+Edit `.env.local` with your development credentials.
+
+## Project Structure
+
+
+```
+cli/
+├── src/
+│ ├── bin.ts # Entry point
+│ ├── app.ts # Stricli application setup
+│ ├── context.ts # Dependency injection context
+│ ├── commands/ # CLI commands
+│ │ ├── agent-conversation/# list, view
+│ │ ├── alert/ # create, delete, edit, list, view
+│ │ ├── auth/ # login, logout, refresh, status, token, whoami
+│ │ ├── build/ # download, upload
+│ │ ├── cli/ # completion, defaults, feedback, fix, import, setup, uninstall, upgrade
+│ │ ├── code-mappings/# upload
+│ │ ├── dart-symbol-map/# upload
+│ │ ├── dashboard/ # add, create, delete, edit, list, restore, revisions, view
+│ │ ├── debug-files/ # bundle-jvm, bundle-sources, check, find, print-sources, upload
+│ │ ├── docs/ # list, query
+│ │ ├── event/ # list, send, view
+│ │ ├── feedback/ # list, view
+│ │ ├── issue/ # archive, events, explain, list, merge, plan, resolve, unresolve, view
+│ │ ├── local/ # run, serve
+│ │ ├── log/ # list, view
+│ │ ├── monitor/ # list, run
+│ │ ├── org/ # list, view
+│ │ ├── platform/ # list
+│ │ ├── proguard/ # upload, uuid
+│ │ ├── project/ # create, delete, list, view
+│ │ ├── react-native/# gradle, xcode
+│ │ ├── release/ # archive, create, delete, deploy, deploys, finalize, list, propose-version, restore, set-commits, view
+│ │ ├── replay/ # list, view
+│ │ ├── repo/ # list
+│ │ ├── snapshots/ # diff, download, upload
+│ │ ├── sourcemap/ # inject, resolve, upload
+│ │ ├── span/ # list, view
+│ │ ├── status/ # show
+│ │ ├── team/ # list
+│ │ ├── trace/ # list, logs, view
+│ │ ├── trial/ # list, start
+│ │ ├── api.ts # Make an authenticated API request
+│ │ ├── explore.ts # Query aggregate event data (Explore)
+│ │ ├── help.ts # Help command
+│ │ ├── info.ts # Print configuration and verify authentication
+│ │ ├── init.ts # Initialize Sentry in your project (experimental)
+│ │ ├── schema.ts # Browse the Sentry API schema
+│ │ └── wasm-split.ts# Add build ids to WebAssembly modules and split out debug data
+│ ├── lib/ # Shared utilities
+│ └── types/ # TypeScript types and Valibot schemas
+├── test/ # Test files (mirrors src/ structure)
+├── script/ # Build and utility scripts
+├── plugins/ # Agent skill files
+└── docs/ # Documentation site (Astro + Starlight)
+```
+
+
+## Building
+
+
+```bash
+# Build for current platform (uses esbuild + fossilize for Node SEA packaging)
+pnpm run build
+
+# Build for all platforms
+pnpm run build:all
+
+# Create npm bundle
+pnpm run bundle
+```
+
+
+## Testing
+
+```bash
+# Run all tests
+pnpm run test
+
+# Run specific test file
+pnpm run test -- test/path/to/test.ts
+
+# Run with watch mode
+pnpm run test -- --watch
+
+# Run with coverage
+pnpm run test -- --coverage
+```
+
+## Code Style
+
+The project uses [Ultracite](https://github.com/getsentry/ultracite) for linting and formatting:
+
+```bash
+# Check for issues
+pnpm run lint
+
+# Auto-fix issues
+pnpm run lint:fix
+
+# Type checking
+pnpm run typecheck
+```
+
+## Submitting Changes
+
+1. Fork the repository
+2. Create a feature branch: `git checkout -b feat/my-feature`
+3. Make your changes
+4. Run tests and linting: `pnpm run test && pnpm run lint`
+5. Commit with [conventional commits](https://www.conventionalcommits.org/): `git commit -m "feat: add new feature"`
+6. Push and create a pull request
+
+## Conventional Commits
+
+We use conventional commits for automatic changelog generation:
+
+- `feat:` - New features
+- `fix:` - Bug fixes
+- `docs:` - Documentation changes
+- `refactor:` - Code refactoring
+- `test:` - Test changes
+- `chore:` - Maintenance tasks
+
+## Getting Help
+
+- [GitHub Issues](https://github.com/getsentry/cli/issues) - Bug reports and feature requests
+- [GitHub Discussions](https://github.com/getsentry/cli/discussions) - Questions and discussions
diff --git a/apps/cli-docs/src/content/docs/exit-codes.md b/apps/cli-docs/src/content/docs/exit-codes.md
new file mode 100644
index 000000000..715268c69
--- /dev/null
+++ b/apps/cli-docs/src/content/docs/exit-codes.md
@@ -0,0 +1,100 @@
+---
+title: Exit Codes
+description: Exit code reference for scripting and automation with the Sentry CLI
+---
+
+The CLI uses semantic exit codes so scripts, CI pipelines, and AI agents can
+react to failure categories without parsing stderr.
+
+## Exit Code Ranges
+
+| Range | Category | Description |
+|-------|----------|-------------|
+| 0 | Success | Command completed successfully |
+| 1 | General | Unexpected or unclassified error |
+| 10–19 | Auth | Authentication and authorization failures |
+| 20–29 | Input | Configuration, validation, and resolution errors |
+| 30–39 | API | Sentry API and network errors |
+| 40–49 | Feature | Feature availability and billing issues |
+| 50–59 | Operations | Upgrade and OAuth flow errors |
+| 60–69 | Command | Command-specific non-standard exits |
+
+## Complete Reference
+
+| Code | Name | Description |
+|------|------|-------------|
+| 0 | Success | Command completed successfully |
+| 1 | General Error | Unexpected error or unclassified failure |
+| 10 | Not Authenticated | No credentials found — run `sentry auth login` |
+| 11 | Token Expired | Auth token expired — re-authenticate |
+| 12 | Token Invalid | Auth token rejected by the server |
+| 13 | Host Scope | Request blocked — credentials don't match the target host |
+| 20 | Config Error | Configuration or DSN problem |
+| 21 | Validation Error | Invalid input (malformed ID, bad flag value, etc.) |
+| 22 | Missing Context | Required context (org, project) could not be determined |
+| 23 | Not Found | A user-provided identifier could not be resolved |
+| 30 | API Error | Sentry API returned an error response |
+| 31 | Timeout | Operation exceeded its time limit |
+| 40 | Seer Not Enabled | Seer is not enabled for the organization |
+| 41 | Seer No Budget | Seer requires a paid plan |
+| 42 | AI Disabled | AI features disabled by organization admin |
+| 50 | Upgrade Error | CLI upgrade operation failed |
+| 51 | Device Flow Error | OAuth device authorization flow failed |
+| 60 | Output Error | Command produced output but the operation failed |
+| 61 | Wizard Error | Interactive setup wizard encountered an error |
+| 62 | Wizard Deps | Wizard dependency installation failed |
+| 63 | Wizard Codemod | Wizard codemod plan or apply failed |
+| 64 | Wizard Verify | User stopped wizard after verification step |
+
+## Scripting Examples
+
+### Bash
+
+```bash
+sentry issue list my-org/
+code=$?
+
+case $code in
+ 0) echo "Success" ;;
+ 1?) echo "Auth problem (code $code) — run: sentry auth login" ;;
+ 2?) echo "Input/config problem (code $code)" ;;
+ 3?) echo "API/network error (code $code)" ;;
+ 4?) echo "Feature not available (code $code)" ;;
+ *) echo "Failed with exit code $code" ;;
+esac
+```
+
+### Python
+
+```python
+import subprocess
+
+result = subprocess.run(["sentry", "issue", "list", "my-org/"], capture_output=True)
+
+if result.returncode == 0:
+ print("Success")
+elif 10 <= result.returncode <= 19:
+ print("Auth error — run: sentry auth login")
+elif 20 <= result.returncode <= 29:
+ print("Input/config error")
+elif 30 <= result.returncode <= 39:
+ print("API/network error")
+elif 40 <= result.returncode <= 49:
+ print("Feature not available")
+```
+
+## Notes
+
+- Exit codes below 128 are safe from collision with Unix signal exits (128+N).
+- The `sentry api` command renders API error responses to stdout and exits
+ with code 60 (Output Error), not 30 (API Error). This matches the `gh api`
+ convention — the error response body is useful output. Parse the HTTP status
+ from `--verbose` output or the JSON error body if you need to distinguish
+ API error categories.
+- The `sentry init` wizard maps its internal workflow exit codes to CLI
+ exit codes: platform not detected → 20 (Config), dependency install
+ failed → 62 (Wizard Deps), codemod failed → 63 (Wizard Codemod),
+ verification stopped → 64 (Wizard Verify), other → 61 (Wizard).
+- [Stricli](https://bloomberg.github.io/stricli/) (the CLI framework) uses
+ negative exit codes (-5 to -1) for framework-level errors like unknown
+ commands or invalid arguments. These appear as 251–255 in unsigned form.
diff --git a/apps/cli-docs/src/content/docs/features.md b/apps/cli-docs/src/content/docs/features.md
new file mode 100644
index 000000000..1e9b0f8aa
--- /dev/null
+++ b/apps/cli-docs/src/content/docs/features.md
@@ -0,0 +1,242 @@
+---
+title: Features
+description: Advanced features of the Sentry CLI
+---
+
+The Sentry CLI includes several features designed to streamline your workflow, especially in complex project setups.
+
+## DSN Auto-Detection
+
+The CLI automatically detects your Sentry project from your codebase, eliminating the need to specify the target for every command. DSN detection is one part of the [resolution priority chain](./configuration/#resolution-priority) — it runs after checking for explicit arguments, environment variables, and `.sentryclirc` config files.
+
+### How It Works
+
+DSN detection follows this priority order (highest first):
+
+1. **Source code** - Explicit DSN in `Sentry.init()` calls
+2. **Environment files** - `.env.local`, `.env`, etc.
+3. **Environment variable** - `SENTRY_DSN`
+
+When a DSN is found, the CLI resolves it to your organization and project, then caches the result for fast subsequent lookups.
+
+:::tip
+For monorepos or when DSN detection picks up the wrong project, use a [`.sentryclirc` config file](./configuration/#configuration-file-sentryclirc) to pin your org/project explicitly.
+:::
+
+### Supported Languages
+
+The CLI scans source files for DSN URLs (the `https://…@….ingest.sentry.io/…` pattern) using a universal regex — no language-specific parsing is needed. Any text file with a recognized extension is scanned, including:
+
+| Language Family | File Extensions |
+|----------------|-----------------|
+| JavaScript/TypeScript | `.js`, `.ts`, `.jsx`, `.tsx`, `.mjs`, `.cjs`, `.astro`, `.vue`, `.svelte` |
+| Python | `.py` |
+| Go | `.go` |
+| JVM (Java, Kotlin, Scala, Groovy) | `.java`, `.kt`, `.kts`, `.scala`, `.groovy` |
+| .NET (C#, F#, VB) | `.cs`, `.fs`, `.vb` |
+| Ruby | `.rb`, `.erb` |
+| PHP | `.php` |
+| Swift/Objective-C | `.swift`, `.m`, `.mm` |
+| Rust | `.rs` |
+| Dart/Flutter | `.dart` |
+| Elixir/Erlang | `.ex`, `.exs`, `.erl` |
+| Config files | `.json`, `.yaml`, `.yml`, `.toml`, `.xml`, `.properties` |
+
+### Caching
+
+To avoid scanning your codebase on every command, the CLI caches:
+
+- **DSN location** - Which file contains the DSN
+- **Resolved project** - The org/project slugs from the API
+
+The cache is validated on each run by checking if the source file still contains the same DSN. If the DSN changes or the file is deleted, a full scan is triggered.
+
+### Usage
+
+Once your project has a DSN configured, commands automatically use it:
+
+```bash
+# Instead of:
+sentry issue list my-org/my-project
+
+# Just run:
+sentry issue list
+```
+
+The CLI will show which project was detected:
+
+```
+Detected project: my-app (from .env)
+
+ID SHORT ID TITLE COUNT
+123456789 MYAPP-ABC TypeError: Cannot read prop... 142
+```
+
+## Monorepo Support & Alias System
+
+In monorepos with multiple Sentry projects, the CLI generates short aliases for each project, making it easy to work with issues across projects.
+
+### How Aliases Work
+
+When you run `sentry issue list`, the CLI:
+
+1. Scans for DSNs in monorepo directories (`packages/`, `apps/`, etc.)
+2. Generates unique short aliases for each project
+3. Caches the aliases for use with other commands
+
+Aliases are the shortest unique prefix of each project slug. For example:
+
+| Project Slug | Alias |
+|--------------|-------|
+| `frontend` | `f` |
+| `functions` | `fu` |
+| `backend` | `b` |
+
+For projects with a common prefix (like `spotlight-electron`, `spotlight-website`), the prefix is stripped first:
+
+| Project Slug | Alias |
+|--------------|-------|
+| `spotlight-electron` | `e` |
+| `spotlight-website` | `w` |
+| `spotlight-backend` | `b` |
+
+### Using Alias-Suffix Format
+
+After running `issue list`, you can reference issues using the `alias-suffix` format:
+
+```bash
+# List issues - note the ALIAS column
+sentry issue list
+```
+
+```
+ALIAS SHORT ID TITLE COUNT
+e SPOTLIGHT-ELEC-4Y TypeError: Cannot read prop... 142
+w SPOTLIGHT-WEB-ABC Failed to fetch user data 89
+b SPOTLIGHT-BACK-XYZ Connection timeout 34
+```
+
+```bash
+# View issue using alias-suffix
+sentry issue view e-4Y
+
+# Explain using alias-suffix
+sentry issue explain w-ABC
+
+# Works with any issue command
+sentry issue plan b-XYZ
+```
+
+### Cross-Organization Support
+
+If you work with multiple organizations that have projects with the same slug, the CLI uses org-prefixed aliases:
+
+```
+ALIAS SHORT ID TITLE
+o1:api ORG1-API-123 Error in API handler
+o2:api ORG2-API-456 Database connection failed
+```
+
+## Issue ID Formats
+
+The CLI accepts several formats for identifying issues:
+
+### Numeric ID
+
+The internal Sentry issue ID:
+
+```bash
+sentry issue view 123456789
+sentry issue explain 987654321
+```
+
+### Full Short ID
+
+The project-prefixed short ID shown in Sentry UI:
+
+```bash
+sentry issue view MYPROJECT-ABC
+sentry issue explain FRONTEND-XYZ
+```
+
+### Short Suffix
+
+Just the suffix portion when project context is provided via the `/` prefix:
+
+```bash
+sentry issue view my-org/myproject-ABC
+```
+
+### GitHub-Style (`#` separator)
+
+A `#` may be used in place of the final slash, matching how issues are referenced
+on GitHub. This is handy for AI agents and tooling that emit `org/project#SHORTID`:
+
+```bash
+# Equivalent to my-org/my-project/PROJ-123
+sentry issue view my-org/my-project#PROJ-123
+
+# Project context only (org auto-detected)
+sentry issue view my-project#PROJ-123
+```
+
+### Alias-Suffix
+
+The short alias plus suffix, available after running `issue list`:
+
+```bash
+# First, list issues to populate the alias cache
+sentry issue list
+
+# Then use alias-suffix format
+sentry issue view e-4Y
+sentry issue explain w-ABC
+sentry issue plan b-XYZ
+```
+
+This format is especially useful in monorepos where you're working across multiple projects.
+
+## AI-Powered Analysis with Seer
+
+The CLI integrates with Sentry's Seer AI to provide root cause analysis and fix plans directly in your terminal.
+
+### Root Cause Analysis
+
+Use `sentry issue explain` to understand why an issue is happening:
+
+```bash
+sentry issue explain MYPROJECT-ABC
+```
+
+Seer analyzes:
+- Stack traces and error messages
+- Related events and patterns
+- Your codebase (via GitHub integration)
+
+And provides:
+- Detailed root cause explanation
+- Reproduction steps
+- Relevant code locations
+
+### Fix Plans
+
+After understanding the root cause, use `sentry issue plan` to get actionable fix steps:
+
+```bash
+sentry issue plan MYPROJECT-ABC
+```
+
+The plan includes:
+- Specific files to modify
+- Code changes to make
+- Implementation guidance
+
+### Requirements
+
+For Seer integration to work, you need:
+
+1. **Seer enabled** for your organization
+2. **GitHub integration** configured with repository access
+3. **Code mappings** set up to link stack frames to source files
+
+See [Sentry's Seer documentation](https://docs.sentry.io/product/issues/issue-details/ai-suggested-solution/) for setup instructions.
diff --git a/apps/cli-docs/src/content/docs/getting-started.mdx b/apps/cli-docs/src/content/docs/getting-started.mdx
new file mode 100644
index 000000000..b9dc05975
--- /dev/null
+++ b/apps/cli-docs/src/content/docs/getting-started.mdx
@@ -0,0 +1,201 @@
+---
+title: Installation
+description: How to install and set up the Sentry CLI
+---
+
+import PackageManagerCode from "../../components/PackageManagerCode.astro";
+
+## Install Script
+
+Install the latest stable release:
+
+```bash
+curl https://cli.sentry.dev/install -fsS | bash
+```
+
+Install the nightly build (built from `main`, updated on every commit):
+
+```bash
+curl https://cli.sentry.dev/install -fsS | bash -s -- --version nightly
+```
+
+You can also use the `SENTRY_VERSION` environment variable to pin a version,
+which is especially useful in CI/CD pipelines and Dockerfiles:
+
+```bash
+# Pin to a specific stable version
+curl https://cli.sentry.dev/install -fsS | SENTRY_VERSION=0.42.2 bash
+
+# Pin to nightly
+curl https://cli.sentry.dev/install -fsS | SENTRY_VERSION=nightly bash
+```
+
+The `--version` flag takes precedence over `SENTRY_VERSION` if both are set.
+The chosen channel is persisted so that `sentry cli upgrade` automatically
+tracks the same channel on future updates.
+
+### Installer Flags
+
+The install script accepts additional flags to customize behavior:
+
+```bash
+# Skip shell config modifications (~/.zshrc, ~/.bashrc, etc.)
+curl https://cli.sentry.dev/install -fsS | bash -s -- --no-modify-path
+
+# Skip shell completion installation
+curl https://cli.sentry.dev/install -fsS | bash -s -- --no-completions
+
+# Skip AI agent skill installation
+curl https://cli.sentry.dev/install -fsS | bash -s -- --no-agent-skills
+```
+
+You can also set `SENTRY_INSTALL_DIR` to override the binary installation directory:
+
+```bash
+curl https://cli.sentry.dev/install -fsS | SENTRY_INSTALL_DIR="$HOME/.local/bin" bash
+```
+
+### Supported Platforms
+
+{/* GENERATED:START platform-support */}
+
+
+
+
+
OS
+
Architectures
+
Notes
+
+
+
+
+
macOS
+
x64, arm64 (Apple Silicon)
+
+
+
+
Linux
+
x64, arm64
+
Standalone binary requires glibc
+
+
+
Windows
+
x64
+
Via Git Bash, MSYS2, or WSL
+
+
+
+
+{/* GENERATED:END platform-support */}
+
+On Alpine and other musl-based Linux systems, install Node.js and use
+`npm install -g sentry` instead of the standalone installer.
+
+## Homebrew
+
+```bash
+brew install getsentry/tools/sentry
+```
+
+## Package Managers
+
+Install globally with your preferred package manager (the npm packages require **Node.js 20+**; on **22.15+** the built-in `node:sqlite` is used, on 20–22.14 a bundled WASM fallback is used transparently):
+
+
+
+Unlike the install script and Homebrew, package manager installs don't set up shell completions or agent skills. Run `sentry cli setup` once to enable them:
+
+```bash
+sentry cli setup
+```
+
+Or run directly without installing:
+
+
+
+## Authentication
+
+### OAuth Device Flow (Recommended)
+
+On a fresh installation, the install script starts OAuth login automatically
+when running in an interactive terminal with no detected AI agent or existing
+credentials. Upgrades and non-interactive installations skip this first-login step.
+
+The easiest way to authenticate is via OAuth device flow:
+
+```bash
+sentry auth
+```
+
+You'll be given a URL and a code to enter. Once you authorize the application
+in your browser, the CLI stores the OAuth credentials. When the server provides
+a refresh token, the CLI refreshes the access token automatically. Persist the
+Sentry CLI configuration directory (`$XDG_CONFIG_HOME/sentry/`, defaulting to
+`~/.config/sentry/`, overridable with `SENTRY_CONFIG_DIR`) across runs to keep
+automatic refresh working.
+
+### API Token
+
+Alternatively, you can use an API token directly:
+
+```bash
+sentry auth --token YOUR_SENTRY_API_TOKEN
+```
+
+You can create API tokens in your [Sentry account settings](https://sentry.io/settings/account/api/auth-tokens/).
+
+API tokens are also useful for ephemeral CI jobs and sandboxes that cannot
+persist the CLI configuration directory.
+
+### Check Auth Status
+
+Verify your authentication status:
+
+```bash
+sentry auth status
+```
+
+### Logout
+
+To remove stored credentials:
+
+```bash
+sentry auth logout
+```
+
+## Self-Hosted Sentry
+
+Using a self-hosted Sentry instance? Create a public OAuth application and pass
+its client ID and instance URL when you log in:
+
+```bash
+SENTRY_CLIENT_ID=your-client-id sentry auth login --url https://sentry.example.com
+```
+
+See the [Self-Hosted](../self-hosted/) guide for full setup details.
+
+## Configuration
+
+Credentials are stored in a SQLite database under `$XDG_CONFIG_HOME/sentry/` (defaulting to `~/.config/sentry/`) with restricted file permissions (mode 600) for security. See [Configuration](../configuration/) for environment variables and customization options.
+
+## Next Steps
+
+Once authenticated, you can start using the CLI:
+
+- [Initialize Sentry](../commands/init/) - Set up Sentry in your project with the guided wizard
+- [Organization commands](../commands/org/) - List and view organizations
+- [Project commands](../commands/project/) - Manage projects
+- [Issue commands](../commands/issue/) - Track and manage issues
+- [Event commands](../commands/event/) - Inspect events
+- [API commands](../commands/api/) - Direct API access
+- [Agentic Usage](../agentic-usage/) - Enable AI coding agents to use the CLI
diff --git a/apps/cli-docs/src/content/docs/index.mdx b/apps/cli-docs/src/content/docs/index.mdx
new file mode 100644
index 000000000..4fbff5831
--- /dev/null
+++ b/apps/cli-docs/src/content/docs/index.mdx
@@ -0,0 +1,107 @@
+---
+title: The CLI for developers and agents
+description: A CLI for developers and agents
+template: splash
+prev: false
+next: false
+hero:
+ title: |
+ The CLI for
+ developers and agents
+ tagline: No more flags. No more guessing. Just type what you mean and let the CLI figure out the rest. Built by humans and agents, for humans and agents.
+---
+
+import { Card, CardGrid } from '@astrojs/starlight/components';
+import InstallSelector from '../../components/InstallSelector.astro';
+import Terminal from '../../components/Terminal.astro';
+import FeatureTerminal from '../../components/FeatureTerminal.astro';
+import FeatureVisual from '../../components/FeatureVisual.astro';
+import sectionBg1 from '../../assets/section-bg-1.png';
+import sectionBg2 from '../../assets/section-bg-2.png';
+import sectionBg3 from '../../assets/section-bg-3.png';
+
+
+
+
+
+{/* ============================================
+ Features Section - Horizontal Rows with Terminals
+ ============================================ */}
+
+
+
+
+
It Knows Your Project
+
No config files. No flags. The CLI reads your .env, detects your project from the codebase, and just works. Monorepos, multiple orgs, complex setups — all handled automatically.
+
Stop memorizing project slugs and DSNs. Start typing commands that make sense.
+
+
+
+
$sentry issue list
+
Detected project: my-app (from .env)
+
+
╭─────────┬────────────┬──────────────────────────────────────────┬───────╮
+│LEVEL│SHORT ID│TITLE│COUNT│
+├─────────┼────────────┼──────────────────────────────────────────┼───────┤
+│ERROR│ MYAPP-WQ │TypeError: Cannot read property 'map'...│ 142 │
+│WARN│ MYAPP-X3 │Failed to fetch user data from API│ 89 │
+│ERROR│ MYAPP-R7 │Connection timeout after 30s│ 34 │
+╰─────────┴────────────┴──────────────────────────────────────────┴───────╯
+
+
+
+
+
+
+
+
+
Ask Seer Why
+
Get AI-powered root cause analysis right in your terminal. Seer analyzes stack traces, related events, and your codebase to explain exactly what went wrong and why.
+
Then run sentry issue plan to get a step-by-step fix you can apply immediately.
+
+
+
+
$sentry issue explain WQ
+
Analyzing MYAPP-WQ...
+
+
Root Cause: The user object is undefined when
+
accessed before the auth check completes in useEffect.
+
+
Affected: src/hooks/useUser.ts:42
+
Run `sentry issue plan` for a fix.
+
+
+
+
+
+
+
+
+
Works With Everything
+
Structured JSON output for scripts and pipelines. Open issues directly in your browser. Pipe to jq, fzf, or your favorite tools.
+
Built for humans and AI agents alike — every command is predictable, composable, and automation-ready.
+
+
+
+
$sentry org list --json | jq '.[0]'
+
+
{"{"}
+
"slug": "my-org",
+
"name": "My Organization",
+
"projects": 12,
+
"members": 8
+
{"}"}
+
+
+
+
\ No newline at end of file
diff --git a/apps/cli-docs/src/content/docs/library-usage.md b/apps/cli-docs/src/content/docs/library-usage.md
new file mode 100644
index 000000000..c72bba3cd
--- /dev/null
+++ b/apps/cli-docs/src/content/docs/library-usage.md
@@ -0,0 +1,272 @@
+---
+title: Library Usage
+description: Use the Sentry CLI programmatically in Node.js
+---
+
+The Sentry CLI can be used as a JavaScript/TypeScript library, running commands
+in-process without spawning a subprocess. This is useful for AI coding agents,
+build tools, CI scripts, and other tools that want structured Sentry data.
+
+## Installation
+
+```bash
+npm install sentry
+```
+
+## Quick Start
+
+```typescript
+import createSentrySDK from "sentry";
+
+const sdk = createSentrySDK({ token: "sntrys_..." });
+
+// Typed methods for every CLI command
+const orgs = await sdk.org.list();
+const issues = await sdk.issue.list({ orgProject: "acme/frontend", limit: 5 });
+```
+
+## Typed SDK
+
+`createSentrySDK()` returns an object with typed methods for **every** CLI command,
+organized by the CLI route hierarchy:
+
+```typescript
+import createSentrySDK from "sentry";
+
+const sdk = createSentrySDK({ token: "sntrys_..." });
+```
+
+### Organizations
+
+```typescript
+const orgs = await sdk.org.list();
+const org = await sdk.org.view({ org: "acme" });
+```
+
+### Projects
+
+```typescript
+const projects = await sdk.project.list({ orgProject: "acme/" });
+const project = await sdk.project.view({ orgProject: "acme/frontend" });
+```
+
+### Issues
+
+```typescript
+const issues = await sdk.issue.list({
+ orgProject: "acme/frontend",
+ limit: 10,
+ query: "is:unresolved",
+ sort: "date",
+});
+
+const issue = await sdk.issue.view({}, "ACME-123");
+```
+
+### Events, Traces, Spans
+
+```typescript
+const event = await sdk.event.view({}, "abc123...");
+
+const traces = await sdk.trace.list({ orgProject: "acme/frontend" });
+const trace = await sdk.trace.view({}, "abc123...");
+
+const spans = await sdk.span.list({}, "acme/frontend");
+```
+
+### Dashboards
+
+```typescript
+const dashboards = await sdk.dashboard.list({}, "acme/");
+const dashboard = await sdk.dashboard.view({}, "acme/", "my-dashboard");
+
+// Nested widget commands
+await sdk.dashboard.widget.add(
+ { display: "line", query: ["count"] },
+ "acme/", "my-dashboard", "Errors over time"
+);
+```
+
+### Teams
+
+```typescript
+const teams = await sdk.team.list({ orgProject: "acme/" });
+```
+
+### Authentication
+
+```typescript
+await sdk.auth.login();
+await sdk.auth.status();
+const whoami = await sdk.auth.whoami();
+```
+
+The typed SDK invokes command handlers directly — bypassing CLI string parsing
+for zero overhead beyond the command's own logic.
+
+## Escape Hatch: `run()`
+
+For commands not easily expressed through the typed API, or when you want to
+pass raw CLI flags, use `sdk.run()`:
+
+```typescript
+// Run any CLI command — returns parsed JSON by default
+const version = await sdk.run("--version");
+const issues = await sdk.run("issue", "list", "-l", "5");
+const help = await sdk.run("help", "issue");
+```
+
+## Authentication
+
+The `token` option provides an auth token for the current invocation. When
+omitted, it falls back to stored credentials and environment variables:
+
+1. `token` option (highest priority)
+2. Stored OAuth token from `sentry auth login` (if not expired)
+3. `SENTRY_AUTH_TOKEN` environment variable
+4. `SENTRY_TOKEN` environment variable
+
+Set `SENTRY_FORCE_ENV_TOKEN=1` to make environment variable tokens take priority over stored OAuth.
+
+```typescript
+// Explicit token
+const sdk = createSentrySDK({ token: "sntrys_..." });
+
+// Or set the env var — it's picked up automatically
+process.env.SENTRY_AUTH_TOKEN = "sntrys_...";
+const sdk = createSentrySDK();
+```
+
+## Options
+
+All options are optional. Pass them when creating the SDK:
+
+```typescript
+const sdk = createSentrySDK({ token: "...", text: true, cwd: "/my/project" });
+```
+
+| Option | Type | Default | Description |
+|--------|------|---------|-------------|
+| `token` | `string` | Auto-detected | Auth token for this invocation |
+| `url` | `string` | `sentry.io` | Sentry instance URL for self-hosted |
+| `org` | `string` | Auto-detected | Default organization slug |
+| `project` | `string` | Auto-detected | Default project slug |
+| `text` | `boolean` | `false` | Return human-readable text instead of parsed JSON (`run()` only) |
+| `cwd` | `string` | `process.cwd()` | Working directory for DSN auto-detection |
+| `headers` | `Record` | — | Extra HTTP headers for self-hosted instances behind a reverse proxy (same as [`SENTRY_CUSTOM_HEADERS`](./configuration/#sentry_custom_headers)); ignored for sentry.io |
+| `signal` | `AbortSignal` | — | Abort signal for cancelling streaming commands |
+
+## Return Values
+
+Typed SDK methods return **parsed JavaScript objects** with zero serialization
+overhead (via zero-copy capture). The `run()` escape hatch returns parsed JSON
+by default, or a trimmed string for commands without JSON support.
+
+```typescript
+// Typed methods → typed return
+const issues = await sdk.issue.list({ orgProject: "acme/frontend" });
+// IssueListResult type with known fields
+
+// run() → parsed JSON or string
+const version = await sdk.run("--version");
+// "sentry 0.21.0"
+```
+
+## Error Handling
+
+Commands that fail throw a `SentryError`:
+
+```typescript
+import createSentrySDK, { SentryError } from "sentry";
+
+const sdk = createSentrySDK();
+
+try {
+ await sdk.issue.view({}, "NONEXISTENT-1");
+} catch (err) {
+ if (err instanceof SentryError) {
+ console.error(err.message); // Clean error message (no ANSI codes)
+ console.error(err.exitCode); // Non-zero exit code
+ console.error(err.stderr); // Raw stderr output
+ }
+}
+```
+
+## Environment Isolation
+
+The library never mutates `process.env`. Each invocation creates an isolated
+copy of the environment. This means:
+
+- Your application's env vars are never touched
+- Multiple sequential calls are safe
+- Auth tokens passed via `token` don't leak to subsequent calls
+
+:::note
+Concurrent calls are not supported in the current version.
+Calls should be sequential (awaited one at a time).
+:::
+
+## Comparison with Subprocess
+
+| | Library (`createSentrySDK()`) | Subprocess (`child_process`) |
+|---|---|---|
+| **Startup** | ~0ms (in-process) | ~200ms (process spawn + init) |
+| **Output** | Parsed object (zero-copy) | String (needs JSON.parse) |
+| **Errors** | `SentryError` with typed fields | Exit code + stderr string |
+| **Auth** | `token` option or env vars | Env vars only |
+| **Node.js** | >=20 required | Any version |
+
+## Requirements
+
+- **Node.js >= 20**. On Node.js 22.15+ the built-in `node:sqlite` module is used; on Node.js 20–22.14 the CLI transparently falls back to a bundled WASM SQLite driver, so no native module or extra install step is required.
+
+:::caution
+The WASM SQLite fallback (Node.js 20–22.14) does not support [WAL mode](https://www.sqlite.org/wal.html), so concurrent access from multiple processes is slower and its local cache reads/writes are less efficient. For the best performance and reliability we **strongly recommend** the [standalone binary](/installation/) (which bundles a modern runtime) or running on **Node.js 22.15+** so the native `node:sqlite` driver is used.
+:::
+
+## Streaming Commands
+
+Two commands support real-time streaming: `log list --follow` and `dashboard view --refresh`.
+When using streaming flags, methods return an `AsyncIterable` instead of a `Promise`:
+
+```typescript
+const sdk = createSentrySDK({ token: "sntrys_..." });
+
+// Stream logs as they arrive (polls every 5 seconds)
+for await (const log of sdk.log.list({ follow: "5" }, "acme/backend")) {
+ console.log(log);
+}
+
+// Auto-refresh dashboard (polls every 30 seconds)
+for await (const snapshot of sdk.run("dashboard", "view", "123", "--refresh", "30")) {
+ console.log(snapshot);
+}
+
+// Stop streaming by breaking out of the loop
+for await (const log of sdk.log.list({ follow: "2" })) {
+ if (someCondition) break; // Streaming stops immediately
+}
+```
+
+### Cancellation
+
+`break` in a `for await...of` loop immediately signals the streaming command to stop.
+You can also pass an `AbortSignal` via `SentryOptions` for programmatic cancellation:
+
+```typescript
+const controller = new AbortController();
+const sdk = createSentrySDK({ token: "...", signal: controller.signal });
+
+// Cancel after 30 seconds
+setTimeout(() => controller.abort(), 30_000);
+
+for await (const log of sdk.log.list({ follow: "5" })) {
+ console.log(log);
+}
+// Loop exits when signal fires
+```
+
+:::note
+Concurrent streaming calls are not supported. Each streaming invocation
+uses an isolated environment — only one can be active at a time.
+:::
diff --git a/apps/cli-docs/src/content/docs/migrating-from-v3.md b/apps/cli-docs/src/content/docs/migrating-from-v3.md
new file mode 100644
index 000000000..588464f97
--- /dev/null
+++ b/apps/cli-docs/src/content/docs/migrating-from-v3.md
@@ -0,0 +1,616 @@
+---
+title: Migrating from v3 (sentry-cli)
+description: Upgrade guide from the legacy sentry-cli (v3) to the new sentry CLI (v4), including the sentry-cli → sentry rename, command changes, and copy-paste compatibility shims.
+---
+
+Version 4 is a ground-up rewrite of the Sentry CLI. The most visible change is
+the name: the tool is now called **`sentry`** (not `sentry-cli`), shipped from
+the **`sentry`** npm package (not `@sentry/cli`). Most of your commands keep
+working — many old names are kept as hidden aliases — but a handful moved, and
+the output/exit-code behavior was modernized.
+
+This guide covers everything that changed and gives you **copy-paste shims** so
+existing scripts and muscle memory keep working while you migrate.
+
+:::tip[In a hurry?]
+Add the [compatibility shim](#drop-in-compatibility-shim) to your shell profile
+and most `sentry-cli …` invocations keep working unchanged. Then migrate at your
+own pace.
+:::
+
+## What changed at a glance
+
+| Area | v3 (`sentry-cli`) | v4 (`sentry`) |
+|------|-------------------|---------------|
+| Binary name | `sentry-cli` | `sentry` |
+| npm package | `@sentry/cli` | `sentry` |
+| Command groups | plural (`releases`, `projects`) | singular (`release`, `project`) |
+| Output | plain text | Markdown on a TTY, plain when piped, `--json` for machines |
+| Exit codes | mostly `1` | [semantic ranges](/exit-codes/) (auth=1x, input=2x, API=3x…) |
+| Auth | token-only | OAuth device flow (`sentry auth login`) **or** token |
+| Some commands | `login`, `update`, `upload-dif`, `deploys` | moved (see [table](#command-changes)) |
+| Flags | per-command `--auth-token`/`--url`/`--header`/… | `--org`/`--project`/`--log-level`/`-v` kept; others → env vars (see [Global flags](#global-flags-and-options)) |
+
+Your **environment variables** (`SENTRY_AUTH_TOKEN`, `SENTRY_ORG`,
+`SENTRY_PROJECT`, `SENTRY_DSN`, `SENTRY_URL`) and your **`.sentryclirc`** file
+are still read, so CI credentials keep working as-is.
+
+## Installation
+
+Uninstall the old package/binary and install the new one.
+
+```bash
+# npm / pnpm / yarn / bun
+npm uninstall -g @sentry/cli
+npm install -g sentry # or: pnpm add -g sentry / yarn global add sentry / bun add -g sentry
+
+# Homebrew
+brew uninstall sentry-cli
+brew install getsentry/tools/sentry
+
+# Install script
+curl https://cli.sentry.dev/install -fsS | bash
+
+# One-off, no install
+npx sentry@latest --help
+```
+
+Verify:
+
+```bash
+sentry --version
+sentry auth status
+```
+
+## The `sentry-cli` → `sentry` rename
+
+If you only ever call the top-level binary, the simplest bridge is a plain
+alias so old commands and scripts resolve to the new binary:
+
+```bash
+# ~/.bashrc or ~/.zshrc
+alias sentry-cli='sentry'
+```
+
+This is enough **if** you only used commands whose names didn't move (see the
+[table below](#command-changes)). For the commands that did move, use the
+[compatibility shim](#drop-in-compatibility-shim) instead of a plain alias.
+
+For CI, either update your install step to `npm install -g sentry` and call
+`sentry`, or add a one-line shim in your job:
+
+```bash
+# GitHub Actions / any CI shell
+npm install -g sentry
+sentry-cli() { sentry "$@"; } # if you didn't use any moved commands
+```
+
+## Command changes
+
+### Still work unchanged
+
+These are identical, or the old plural form still works as a shortcut:
+
+| v3 | v4 |
+|----|----|
+| `sentry-cli info` | `sentry info` |
+| `sentry-cli send-event …` | `sentry send-event …` |
+| `sentry-cli bash-hook` | `sentry bash-hook` |
+| `sentry-cli sourcemaps …` | `sentry sourcemaps …` (or `sentry sourcemap …`) |
+| `sentry-cli debug-files …` | `sentry debug-files …` |
+| `sentry-cli react-native gradle` | `sentry react-native gradle` |
+| `sentry-cli react-native xcode` | `sentry react-native xcode` |
+
+### Renamed groups (plural → singular)
+
+Command **groups** are singular now. The plural name still works as a shortcut
+for the bare **list** (`sentry releases` → lists releases), but any
+**subcommand** must use the singular form:
+
+| v3 | v4 |
+|----|----|
+| `sentry-cli organizations …` | `sentry org …` |
+| `sentry-cli projects …` | `sentry project …` |
+| `sentry-cli releases …` | `sentry release …` |
+| `sentry-cli issues …` | `sentry issue …` |
+| `sentry-cli monitors …` | `sentry monitor …` |
+| `sentry-cli repos …` | `sentry repo …` |
+| `sentry-cli events …` | `sentry event …` |
+
+```bash
+# v3
+sentry-cli releases new 1.0.0
+# v4
+sentry release new 1.0.0
+```
+
+### Moved commands
+
+These live under a different group now:
+
+| v3 | v4 |
+|----|----|
+| `sentry-cli login` | `sentry auth login` |
+| `sentry-cli logout` | `sentry auth logout` |
+| `sentry-cli update` | `sentry cli upgrade` |
+| `sentry-cli uninstall` | `sentry cli uninstall` |
+| `sentry-cli deploys new …` | `sentry release deploy …` (create) |
+| `sentry-cli deploys list …` | `sentry release deploys …` (list) |
+| `sentry-cli upload-dif …` | `sentry debug-files upload …` |
+| `sentry-cli upload-dsym …` | `sentry debug-files upload …` |
+| `sentry-cli difutil check …` | `sentry debug-files check …` |
+| `sentry-cli upload-proguard …` | `sentry proguard upload …` |
+
+Most of these were already soft-deprecated in v3 (hidden from `--help` in favor
+of `debug-files` / `proguard`); v4 simply drops the legacy top-level spellings.
+
+`sentry-cli send-envelope` also has no direct v4 equivalent. To send an existing
+envelope file, use `sentry event send ./envelope-file --raw` and update any v3
+flags in the call. The shim below cannot translate `send-envelope` for you.
+
+## Drop-in compatibility shim
+
+Paste this shell function into your `~/.bashrc` / `~/.zshrc` (or a CI step). It
+transparently translates every moved/renamed command to its v4 equivalent, so
+existing `sentry-cli …` calls keep working. Anything it doesn't special-case is
+passed straight through to `sentry`.
+
+```bash
+sentry-cli() {
+ # v3 GLOBAL flags that became environment variables in v4 (see the "Global
+ # flags" section below). They precede the command, so translate only the
+ # LEADING run and stop at the first command word — this leaves command-level
+ # flags untouched (notably `--url`, which `release create`/`deploy` use for
+ # the release/deploy URL, not the Sentry host). Other still-valid v4 globals
+ # are collected into `lead` and re-applied before the command.
+ local envs=() lead=() headers="" allow_failure="" login_url="" login_args=()
+ while [ "$#" -gt 0 ]; do
+ case "${1:-}" in
+ --auth-token) envs+=("SENTRY_AUTH_TOKEN=$2" "SENTRY_FORCE_ENV_TOKEN=1"); shift 2 2>/dev/null || shift ;;
+ --auth-token=*) envs+=("SENTRY_AUTH_TOKEN=${1#*=}" "SENTRY_FORCE_ENV_TOKEN=1"); shift ;;
+ --url) login_url="$2"; shift 2 2>/dev/null || shift ;;
+ --url=*) login_url="${1#*=}"; shift ;;
+ # Multiple --header flags merge into one semicolon-separated var.
+ --header) headers="${headers:+$headers; }$2"; shift 2 2>/dev/null || shift ;;
+ --header=*) headers="${headers:+$headers; }${1#*=}"; shift ;;
+ # Still-valid v4 globals: keep them (value-taking ones consume a value).
+ --org|--project|--log-level|--fields) lead+=("$1" "$2"); shift 2 2>/dev/null || shift ;;
+ --org=*|--project=*|--log-level=*|--fields=*) lead+=("$1"); shift ;;
+ -v|--verbose|--json) lead+=("$1"); shift ;;
+ # v3 `--allow-failure` (and SENTRY_ALLOW_FAILURE) is gone in v4 and would
+ # be rejected as unknown. Strip it and emulate its "never fail" behavior.
+ --allow-failure) allow_failure=1; shift ;;
+ *) break ;;
+ esac
+ done
+
+ # v3 accepts auth, headers and allow-failure after the command too. Keep
+ # --url on releases/deploys: there it can name the release or deploy URL.
+ local command="${1:-}" remaining=()
+ if [ -n "$login_url" ]; then
+ if [ "$command" = login ]; then login_args=(--url "$login_url")
+ else envs+=("SENTRY_HOST=$login_url" "SENTRY_URL=$login_url"); fi
+ fi
+ while [ "$#" -gt 0 ]; do
+ case "$1" in
+ --auth-token)
+ [ "$#" -ge 2 ] || { remaining+=("$1"); shift; continue; }
+ envs+=("SENTRY_AUTH_TOKEN=$2" "SENTRY_FORCE_ENV_TOKEN=1"); shift 2 ;;
+ --auth-token=*) envs+=("SENTRY_AUTH_TOKEN=${1#*=}" "SENTRY_FORCE_ENV_TOKEN=1"); shift ;;
+ --header)
+ [ "$#" -ge 2 ] || { remaining+=("$1"); shift; continue; }
+ headers="${headers:+$headers; }$2"; shift 2 ;;
+ --header=*) headers="${headers:+$headers; }${1#*=}"; shift ;;
+ --allow-failure) allow_failure=1; shift ;;
+ --url)
+ if [ "$command" = login ] || [ "$command" = releases ] || [ "$command" = deploys ] || [ "$#" -lt 2 ]; then
+ remaining+=("$1"); shift
+ else
+ envs+=("SENTRY_HOST=$2" "SENTRY_URL=$2"); shift 2
+ fi ;;
+ --url=*)
+ if [ "$command" = login ] || [ "$command" = releases ] || [ "$command" = deploys ]; then
+ remaining+=("$1")
+ else
+ envs+=("SENTRY_HOST=${1#*=}" "SENTRY_URL=${1#*=}")
+ fi
+ shift ;;
+ *) remaining+=("$1"); shift ;;
+ esac
+ done
+ set -- "${remaining[@]}"
+ [ "${SENTRY_ALLOW_FAILURE:-}" = "1" ] && allow_failure=1
+ [ -n "$headers" ] && envs+=("SENTRY_CUSTOM_HEADERS=$headers")
+
+ # `env` runs the real `sentry` (bypassing this function → no recursion),
+ # re-applying any leading global flags before the translated command.
+ local run=(env "${envs[@]}" sentry "${lead[@]}")
+
+ _scli_set_host() {
+ local word inserted="" updated=()
+ for word in "${run[@]}"; do
+ if [ -z "$inserted" ] && [ "$word" = sentry ]; then
+ updated+=("SENTRY_HOST=$1" "SENTRY_URL=$1")
+ inserted=1
+ fi
+ updated+=("$word")
+ done
+ run=("${updated[@]}")
+ }
+
+ # `deploys` handling (top-level or nested under `releases`). Bare/`list` map to
+ # `release deploys` (list); only `new` (create) can't be shimmed — v4 takes the
+ # environment/name as positionals, not v3's `-e`/`-n` flags — so flag those.
+ local deploy_msg='sentry-cli: `deploys new` changed in v4 — environment/name are positionals now:\n sentry release deploy [name] [--url … --started … --finished …]\n'
+ _scli_deploys() {
+ local release="" listed="" dargs=()
+ while [ "$#" -gt 0 ]; do
+ case "$1" in
+ --url)
+ [ "$#" -ge 2 ] || break
+ _scli_set_host "$2"; shift 2 ;;
+ --url=*) _scli_set_host "${1#*=}"; shift ;;
+ *) break ;;
+ esac
+ done
+ while [ "$#" -gt 0 ]; do
+ case "$1" in
+ list) listed=1; shift ;;
+ -r|--release)
+ release="${2:-}"
+ shift 2 2>/dev/null || shift
+ ;;
+ --release=*) release="${1#*=}"; shift ;;
+ new)
+ if [ -z "$listed" ]; then printf '%b' "$deploy_msg" >&2; return 64; fi
+ dargs+=("$1"); shift ;;
+ *) dargs+=("$1"); shift ;;
+ esac
+ done
+ [ -n "$release" ] && dargs=("$release" "${dargs[@]}")
+ "${run[@]}" release deploys "${dargs[@]}"
+ }
+
+ _scli_group() {
+ local grp="$1"; shift
+ # v3 also accepts global flags between a plural group and its subcommand.
+ while [ "$#" -gt 0 ]; do
+ case "$1" in
+ --org|--project|--log-level|--fields)
+ [ "$#" -ge 2 ] || break
+ run+=("$1" "$2"); shift 2 ;;
+ --org=*|--project=*|--log-level=*|--fields=*|-v|--verbose|--json)
+ run+=("$1"); shift ;;
+ --url)
+ [ "$#" -ge 2 ] || break
+ _scli_set_host "$2"; shift 2 ;;
+ --url=*) _scli_set_host "${1#*=}"; shift ;;
+ *) break ;;
+ esac
+ done
+
+ if [ "$grp" = releases ]; then
+ if [ "${1:-}" = deploys ]; then shift; _scli_deploys "$@"; return; fi
+ if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then "${run[@]}" release list "$@";
+ else "${run[@]}" release "$@"; fi
+ return
+ fi
+
+ case "$grp" in
+ organizations) grp=org ;;
+ projects) grp=project ;;
+ issues) grp=issue ;;
+ monitors) grp=monitor ;;
+ repos) grp=repo ;;
+ events) grp=event ;;
+ esac
+ # Some groups default to `view`, so an empty group must explicitly list.
+ if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then "${run[@]}" "$grp" list "$@";
+ else "${run[@]}" "$grp" "$@"; fi
+ }
+
+ _scli_dispatch() {
+ case "${1:-}" in
+ # Moved commands
+ login) shift; "${run[@]}" auth login "${login_args[@]}" "$@" ;;
+ logout) shift; "${run[@]}" auth logout "$@" ;;
+ send-envelope) printf 'sentry-cli: use sentry event send --raw and update v3 flags\n' >&2; return 64 ;;
+ update) shift; "${run[@]}" cli upgrade "$@" ;;
+ uninstall) shift; "${run[@]}" cli uninstall "$@" ;;
+ deploys) shift; _scli_deploys "$@" ;;
+ upload-dif|upload-dsym) shift; "${run[@]}" debug-files upload "$@" ;;
+ upload-proguard) shift; "${run[@]}" proguard upload "$@" ;;
+ difutil) shift; "${run[@]}" debug-files "$@" ;;
+
+ # Renamed groups accept v3 globals before the subcommand.
+ releases|organizations|projects|issues|monitors|repos|events)
+ _scli_group "$@" ;;
+
+ # Everything else is unchanged
+ *) "${run[@]}" "$@" ;;
+ esac
+ }
+
+ # v3's `--allow-failure`/`SENTRY_ALLOW_FAILURE` made any command exit 0. v4
+ # dropped it, so emulate here: swallow a non-zero status when it was set.
+ if [ -n "$allow_failure" ]; then
+ _scli_dispatch "$@" || { printf 'sentry-cli: command failed, but --allow-failure/SENTRY_ALLOW_FAILURE was set — exiting 0\n' >&2; return 0; }
+ else
+ _scli_dispatch "$@"
+ fi
+}
+```
+
+:::note
+`env` runs the real `sentry` binary, bypassing the function (no infinite
+recursion), and applies the translated `--auth-token`/`--url`/`--header` values
+for that one call only. In `fish`, port the same preprocessing + `switch`/`case`
+into a `function sentry-cli … end`.
+:::
+
+## Global flags and options
+
+v3 accepted many of the same flags on (nearly) every command. v4 keeps a small
+set as **global flags** and moves the rest to environment variables — so the
+biggest migration gotcha is flags that silently no longer exist.
+
+### Still global (work on every command)
+
+| v3 flag | v4 |
+|---------|----|
+| `--org ` | `--org` (accepted; also `SENTRY_ORG`) |
+| `--project ` | `--project` (accepted; also `SENTRY_PROJECT`, or `org/project` combo) |
+| `--log-level ` | `--log-level` |
+| — | `-v` / `--verbose` |
+| — | `--json`, `--fields` (new — structured output) |
+
+### Replaced by environment variables
+
+| v3 flag | v4 replacement |
+|---------|----------------|
+| `--auth-token ` | `SENTRY_AUTH_TOKEN` plus `SENTRY_FORCE_ENV_TOKEN=1` to override stored OAuth credentials (or `sentry auth login`) |
+| `--url ` (self-hosted) | `SENTRY_URL` / `SENTRY_HOST`, or pass the URL as a command argument |
+| `--header "K: V"` | `SENTRY_CUSTOM_HEADERS` |
+
+The [compatibility shim](#drop-in-compatibility-shim) above translates
+`--auth-token`, `--url`, and `--header` into these env vars automatically. For
+`--auth-token`, it sets both `SENTRY_AUTH_TOKEN` and
+`SENTRY_FORCE_ENV_TOKEN=1` so the explicit flag overrides stored OAuth
+credentials as it did in v3.
+
+### Dropped
+
+- `--quiet` (and its `--silent` alias) has no direct replacement — pipe the
+ output (non-TTY is plain) or use `--log-level error`.
+- `--allow-failure` (and the `SENTRY_ALLOW_FAILURE` env var), which made any
+ command exit `0` even on error, is **not implemented in v4** and will be
+ rejected as an unknown flag. Handle failures in your own script (e.g.
+ `sentry … || true`); the [shim](#drop-in-compatibility-shim) above emulates
+ the old behavior when it sees the flag or env var.
+- `SENTRY_API_KEY` (the v3 `apiKey` option) and `SENTRY_VCS_REMOTE` (the
+ `vcsRemote` option) are no longer honored — use `SENTRY_AUTH_TOKEN` for auth.
+- `SENTRY_CUSTOM_HEADERS` only applies to self-hosted Sentry; custom headers are
+ ignored for `sentry.io`.
+
+:::caution
+Command-specific flags changed more than the global ones, and some v3 flags
+don't exist in v4 yet. A few notable ones:
+
+- `release set-commits` drops `--ignore-missing` and `--ignore-empty`.
+- `release deploy` takes separate positional arguments for the release,
+ environment, and optional name (`[/] [name]`),
+ not `--env`/`--name`.
+- `sourcemap upload`/`inject` drop several flags (see [Sourcemaps](#sourcemaps)).
+
+Before relying on a flag, confirm it with `sentry --help` — that's the
+authoritative list for v4. If a flag you depend on is missing, please
+[open an issue](https://github.com/getsentry/cli/issues).
+:::
+
+## Node.js wrapper (`SentryCli` class)
+
+v3's `@sentry/cli` package exported a `SentryCli` class for programmatic use:
+
+```js
+// v3
+const SentryCli = require("@sentry/cli");
+const cli = new SentryCli(null, { authToken: process.env.SENTRY_AUTH_TOKEN });
+await cli.releases.new("1.0.0");
+await cli.releases.uploadSourceMaps("1.0.0", { include: ["./dist"] });
+await cli.releases.setCommits("1.0.0", { auto: true });
+await cli.releases.finalize("1.0.0");
+```
+
+v4 does **not** ship the `SentryCli` class. Instead, the `sentry` package is
+itself usable as a library via `createSentrySDK()`, which exposes a typed method
+for **every** command (full reference: [Library Usage](/library-usage/)):
+
+```js
+// v4
+import createSentrySDK from "sentry";
+const sdk = createSentrySDK({ token: process.env.SENTRY_AUTH_TOKEN });
+await sdk.release.create({ orgVersion: "1.0.0" });
+await sdk.sourcemap.upload({ directory: "./dist", release: "1.0.0" });
+await sdk.release["set-commits"]({ orgVersion: "1.0.0", auto: true });
+await sdk.release.finalize({ orgVersion: "1.0.0" });
+```
+
+:::note
+The `sentry` npm package requires **Node.js 20+** (v3's `@sentry/cli` supported
+older runtimes). On Node.js 22.15+ it uses the built-in `node:sqlite` module; on
+Node.js 20–22.14 it transparently falls back to a bundled WASM SQLite driver, so
+no native module or extra install step is needed. The standalone binary bundles
+its own runtime and is unaffected by your installed Node.js version.
+
+For best performance we strongly recommend the standalone binary or Node.js
+22.15+ — the WASM fallback can't use SQLite WAL mode, making its local cache
+slower and less concurrency-friendly.
+:::
+
+Mapping:
+
+| v3 (`@sentry/cli`) | v4 (`sentry`) |
+|--------------------|---------------|
+| `new SentryCli(configFile, { authToken })` | `createSentrySDK({ token })` (`configFile` dropped) |
+| `cli.releases.new(v)` | `sdk.release.create({ orgVersion: v })` |
+| `cli.releases.finalize(v)` | `sdk.release.finalize({ orgVersion: v })` |
+| `cli.releases.setCommits(v, o)` | `sdk.release["set-commits"]({ orgVersion: v, ...o })` |
+| `cli.releases.uploadSourceMaps(v, { include })` | `sdk.sourcemap.upload({ directory, release: v })` |
+| `cli.releases.newDeploy(v, { env, name, url })` | `sdk.release.deploy({ orgVersion: v, environment: env, name, url })` |
+| `cli.releases.proposeVersion()` | `sdk.release["propose-version"]()` |
+| `cli.execute(args)` | `sdk.run(...args)` |
+
+Note the argument reshaping: the v3 `uploadSourceMaps` `include` array becomes
+the `directory` argument of `sourcemap.upload`, and the release is optional
+(sourcemap upload is keyed by debug ID, as it has been since v2).
+
+### Codemod
+
+To automate the mechanical parts of this migration, run the
+[Codemod](https://codemod.com) from your project root (it rewrites the import,
+constructor, and method chain, and inserts `// TODO(sentry-v4): …` comments where
+option shapes changed and need a manual check).
+
+For now, run it from the CLI repo's source — clone the repo, then point the
+Codemod runner at the transform and your project directory:
+
+```bash
+git clone --depth 1 https://github.com/getsentry/cli /tmp/sentry-cli-src
+
+# From your project root (-t . targets the current directory)
+npx codemod@latest jssg run --language typescript \
+ /tmp/sentry-cli-src/codemods/sentry-v3-to-v4/scripts/codemod.ts -t .
+```
+
+:::note
+Once the codemod is published to the [Codemod registry](https://codemod.com), the
+shorter form below will work — until then, use the run-from-source command above:
+
+```bash
+npx codemod@latest @sentry/cli-v3-to-v4
+```
+:::
+
+It rewrites `.js`/`.ts` files in place. Review the diff afterward — argument
+shapes differ (especially for `uploadSourceMaps` and `newDeploy`), so the codemod
+flags those rather than guessing. See
+[`codemods/sentry-v3-to-v4`](https://github.com/getsentry/cli/tree/main/codemods/sentry-v3-to-v4)
+for the source and test fixtures.
+
+## Output and scripting
+
+v4 produces richer human output (Markdown, rendered on a TTY) but stays
+**script-friendly**:
+
+- **Piping / non-TTY** automatically emits plain text — no ANSI codes.
+- **`--json`** on any command emits stable JSON for machines; combine with
+ `--fields` to select columns.
+- Color respects `NO_COLOR`, `FORCE_COLOR`, and `SENTRY_PLAIN_OUTPUT`.
+
+```bash
+# v3: parse text with grep/awk
+sentry-cli releases list | awk '{print $1}'
+
+# v4: query structured JSON
+sentry release list --json | jq -r '.[].version'
+```
+
+If a script parsed the old plain-text tables, switch it to `--json` — it's far
+more robust than screen-scraping.
+
+## Authentication
+
+v4 adds a browser-based **OAuth device flow** for interactive use, while still
+honoring tokens for CI:
+
+```bash
+# Interactive (opens a browser, no token needed)
+sentry auth login
+
+# Check who you are / token validity
+sentry auth status
+sentry auth whoami # or the top-level: sentry whoami
+
+# CI / non-interactive — unchanged from v3
+export SENTRY_AUTH_TOKEN=sntrys_…
+sentry auth status
+```
+
+Stored OAuth credentials take precedence over `SENTRY_AUTH_TOKEN` by default.
+Set `SENTRY_FORCE_ENV_TOKEN=1` when an environment token must override a stored
+login. (v4 also accepts `SENTRY_TOKEN` as an alias for it.)
+
+Note: there is **no `--auth-token` flag** in v4 — authentication comes from
+`sentry auth login`, `SENTRY_AUTH_TOKEN`, or `.sentryclirc`. See
+[Global flags](#global-flags-and-options) below.
+
+## Exit codes
+
+v3 mostly exited `1` on any error. v4 uses [semantic exit
+codes](/exit-codes/) so scripts and CI can branch on the failure category
+(`1x` auth, `2x` input/config, `3x` API/network, `4x` feature/billing, …).
+
+If a script did `sentry-cli … || handle_error`, it still works — any non-zero
+code triggers the fallback. Only update it if you were matching the **specific**
+value `1`.
+
+## Sourcemaps
+
+The command maps `sourcemaps` → `sourcemap` (the plural is aliased, so existing
+invocations keep working):
+
+```bash
+# v3
+sentry-cli sourcemaps upload ./dist
+
+# v4 (either form works)
+sentry sourcemap upload ./dist
+```
+
+Two behavioral differences to be aware of:
+
+- **Positional args:** v3 accepted multiple paths (`[PATHS]...`); v4 takes a
+ single `` (both `upload` and `inject`). Run one invocation per
+ directory.
+- **Flags:** v4 `sourcemap upload` keeps `--release`, `--dist`, `--url-prefix`,
+ `--ext`, `--ignore`, `--ignore-file`, `--strip-prefix`,
+ `--strip-common-prefix`, `--no-rewrite`, `--allow-empty`. These v3 flags are
+ **not present** in v4: `--url-suffix`, `--note`, `--validate`, `--decompress`,
+ `--wait`, `--wait-for`, `--no-sourcemap-reference`, `--debug-id-reference`,
+ `--bundle`, `--bundle-sourcemap`, `--strict`. `sourcemap inject` also drops
+ `--release`. Run `sentry sourcemap upload --help` for the current set.
+- **`--debug-id-reference` is now automatic:** in v3 that flag let `sourcemaps
+ upload` take the debug ID from the linked sourcemap when it couldn't verify
+ one in the bundle itself, for example in binary bundles.
+ v4 does this by default. If a sourcemap already carries `debug_id`
+ — from a bundler plugin configured with `sourcemaps.disable:
+ 'disable-upload'`, or copied across by a tool like React Native's
+ `copy-debugid.js` — `inject` and `upload` adopt that ID rather than generating a
+ new one.
+
+See [`sourcemap`](/commands/sourcemap/) for details.
+
+## Configuration
+
+Configuration precedence is unchanged in spirit and fully backward compatible:
+
+1. CLI flags
+2. `SENTRY_ORG` / `SENTRY_PROJECT` env vars (`SENTRY_PROJECT` accepts
+ `org/project`)
+3. Stored defaults (`sentry auth login` / `sentry cli defaults`)
+4. DSN auto-detection from your source and `.env` files
+5. Directory-name inference
+
+Your existing **`.sentryclirc`** and `SENTRY_*` environment variables are still
+read. See [Configuration](/configuration/) for the full list.
+
+## Getting help
+
+- `sentry --help` — top-level command list
+- `sentry --help` — details and flags for any command
+- [Command reference](/commands/) · [Exit codes](/exit-codes/) ·
+ [Configuration](/configuration/)
+
+If a command you relied on isn't covered here, please
+[open an issue](https://github.com/getsentry/cli/issues) — we want the
+migration to be painless.
diff --git a/apps/cli-docs/src/content/docs/self-hosted.md b/apps/cli-docs/src/content/docs/self-hosted.md
new file mode 100644
index 000000000..0de9b5b48
--- /dev/null
+++ b/apps/cli-docs/src/content/docs/self-hosted.md
@@ -0,0 +1,113 @@
+---
+title: Self-Hosted Sentry
+description: Using the Sentry CLI with a self-hosted Sentry instance
+---
+
+The CLI works with self-hosted Sentry instances. Set the `SENTRY_HOST` (or `SENTRY_URL`) environment variable to point at your instance:
+
+```bash
+export SENTRY_HOST=https://sentry.example.com
+```
+
+## Authenticating
+
+### With OAuth (Sentry 26.1.0+)
+
+The OAuth device flow requires **Sentry 26.1.0 or later** and a public OAuth application registered on your instance.
+
+#### 1. Create a Public OAuth Application
+
+1. In your Sentry instance, go to **Settings → Developer Settings → Applications → Create New Application** (or visit `https://sentry.example.com/settings/account/api/applications/`)
+2. Select **Public** as the application type
+3. Fill in the required fields (name, redirect URL — can be any placeholder URL)
+3. Save the application and copy the **Client ID**
+
+#### 2. Log In
+
+Use the `--url` flag to authenticate against your instance (recommended — this registers the host as trusted):
+
+```bash
+SENTRY_CLIENT_ID=your-client-id sentry auth login --url https://sentry.example.com
+```
+
+Or pass the instance URL via environment variable:
+
+```bash
+SENTRY_HOST=https://sentry.example.com SENTRY_CLIENT_ID=your-client-id sentry auth login --url https://sentry.example.com
+```
+
+:::tip
+You can export both variables in your shell profile so every CLI invocation picks them up:
+
+```bash
+export SENTRY_HOST=https://sentry.example.com
+export SENTRY_CLIENT_ID=your-client-id
+```
+:::
+
+:::note
+The `--url` flag is the most secure way to authenticate with a new host — it is the only way to register a trust anchor for that host. Without it, the CLI refuses to log in to an instance URL that was picked up from an untrusted channel (e.g. a `.sentryclirc` file), protecting you from credential leaks and OAuth phishing.
+:::
+
+### With an API Token
+
+If your instance is on an older version or you prefer not to create an OAuth application, you can use an API token instead:
+
+1. Go to **Settings → Developer Settings → Personal Tokens** in your Sentry instance (or visit `https://sentry.example.com/settings/account/api/auth-tokens/new-token/`)
+2. Create a new token with the following scopes:
+
+`project:read`, `project:write`, `project:admin`, `org:read`, `event:read`, `event:write`, `member:read`, `team:read`, `team:write`, `team:admin`, `alerts:read`, `alerts:write`
+
+3. Pass it to the CLI:
+
+```bash
+SENTRY_HOST=https://sentry.example.com sentry auth login --token YOUR_TOKEN --url https://sentry.example.com
+```
+
+## After Login
+
+Once authenticated, the CLI stores your instance URL — you don't need to set `SENTRY_URL` on every command. All subsequent commands automatically use the correct instance:
+
+```bash
+sentry issue list
+sentry org list
+```
+
+If you pass a self-hosted Sentry URL as a command argument (e.g., an issue or event URL), the CLI detects the instance automatically.
+
+## TLS / Corporate Proxies
+
+If your self-hosted instance sits behind a private CA certificate (common with corporate TLS-intercepting proxies like Zscaler or Netskope), point `NODE_EXTRA_CA_CERTS` at your CA bundle:
+
+```bash
+export NODE_EXTRA_CA_CERTS=/path/to/corporate-ca.pem
+```
+
+You can also persist this so you don't need the env var on every invocation:
+
+```bash
+sentry cli defaults ca-cert /path/to/corporate-ca.pem
+```
+
+If your proxy requires custom HTTP headers (e.g. an IAP token), set them with `SENTRY_CUSTOM_HEADERS` or persist them:
+
+```bash
+sentry cli defaults headers "X-IAP: token"
+```
+
+## Relevant Environment Variables
+
+
+| Variable | Description |
+|----------|-------------|
+| `SENTRY_HOST` | Base URL of your Sentry instance (takes precedence over `SENTRY_URL`) |
+| `SENTRY_URL` | Alias for `SENTRY_HOST` |
+| `SENTRY_CLIENT_ID` | Client ID of your public OAuth application |
+| `SENTRY_CUSTOM_HEADERS` | Custom HTTP headers for proxy/IAP (semicolon-separated `Name: Value` pairs) |
+| `SENTRY_FORCE_ENV_TOKEN` | Force env token over stored OAuth token |
+| `SENTRY_ORG` | Default organization slug |
+| `SENTRY_PROJECT` | Default project slug (supports `org/project` format) |
+| `NODE_EXTRA_CA_CERTS` | Path to PEM file with additional CA certificates (for corporate proxies) |
+
+
+See [Configuration](./configuration/) for the full environment variable reference.
diff --git a/apps/cli-docs/src/fonts/dammit-sans-v0.3-bold.otf b/apps/cli-docs/src/fonts/dammit-sans-v0.3-bold.otf
new file mode 100755
index 000000000..44cf6cfec
Binary files /dev/null and b/apps/cli-docs/src/fonts/dammit-sans-v0.3-bold.otf differ
diff --git a/apps/cli-docs/src/fragments/commands/agent-conversation.md b/apps/cli-docs/src/fragments/commands/agent-conversation.md
new file mode 100644
index 000000000..9ae15932f
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/agent-conversation.md
@@ -0,0 +1,36 @@
+
+
+
+## Examples
+
+### List conversations
+
+```bash
+# List recent agent conversations
+sentry agent-conversation list
+
+# Explicit organization
+sentry agent-conversation list my-org
+
+# Show more, last 24 hours
+sentry agent-conversation list --limit 50 --period 24h
+
+# Filter conversations
+sentry agent-conversation list -q "has:errors"
+
+# Paginate through results
+sentry agent-conversation list my-org -c next
+```
+
+### View a conversation transcript
+
+```bash
+# View full transcript (org auto-detected)
+sentry agent-conversation view conv-123
+
+# Explicit org (slash-separated)
+sentry agent-conversation view my-org/conv-123
+
+# JSON output
+sentry agent-conversation view my-org/conv-123 --json
+```
diff --git a/apps/cli-docs/src/fragments/commands/alert.md b/apps/cli-docs/src/fragments/commands/alert.md
new file mode 100644
index 000000000..ee32b001f
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/alert.md
@@ -0,0 +1,91 @@
+
+
+## Examples
+
+### Create an issue alert rule
+
+```bash
+# Create an issue alert rule with inline JSON condition/action
+sentry alert issues create my-org/my-project \
+ --name "Error Spike" \
+ --condition '{"type":"first_seen_event","comparison":true,"conditionResult":true}' \
+ --action '{"type":"email","data":{},"config":{"targetType":"team","targetIdentifier":"1"}}'
+```
+
+### List issue alert rules
+
+```bash
+# List issue alert rules for a project
+sentry alert issues list my-org/my-project
+
+# Filter rules by name
+sentry alert issues list my-org/my-project --query "spike"
+```
+
+### View an issue alert rule
+
+```bash
+# View by ID
+sentry alert issues view my-org/my-project/12345
+
+# View by name
+sentry alert issues view my-org/my-project/"Error Spike"
+```
+
+### Edit an issue alert rule
+
+```bash
+# Edit issue alert name/status
+sentry alert issues edit my-org/my-project/12345 --name "Prod Error Spike" --status disabled
+```
+
+### Delete an issue alert rule
+
+```bash
+# Delete with preview
+sentry alert issues delete my-org/my-project/12345 --dry-run
+```
+
+### Create a metric alert rule
+
+```bash
+# Create an organization metric alert rule
+sentry alert metrics create my-org \
+ --name "P95 Latency" \
+ --query "environment:prod" \
+ --aggregate "p95(span.duration)" \
+ --dataset spans \
+ --time-window 5 \
+ --trigger '{"alertThreshold":500,"actions":[{"id":"sentry.mail.actions.NotifyEmailAction","targetType":"Team","targetIdentifier":1}]}'
+```
+
+### List metric alert rules
+
+```bash
+# List metric alert rules for an organization
+sentry alert metrics list my-org/
+```
+
+### View a metric alert rule
+
+```bash
+# View by ID
+sentry alert metrics view my-org/67890
+
+# View by name
+sentry alert metrics view my-org/"P95 latency alert"
+```
+
+### Edit a metric alert rule
+
+```bash
+# Edit metric alert query/window
+sentry alert metrics edit my-org/67890 --query "environment:prod event.type:error" --time-window 15
+```
+
+### Delete a metric alert rule
+
+```bash
+# Delete without prompt
+sentry alert metrics delete my-org/67890 --yes
+```
diff --git a/apps/cli-docs/src/fragments/commands/api.md b/apps/cli-docs/src/fragments/commands/api.md
new file mode 100644
index 000000000..e42ad8600
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/api.md
@@ -0,0 +1,77 @@
+
+
+## Examples
+
+Endpoints are relative to `/api/0/` — the prefix is added automatically. Absolute HTTP(S) Sentry URLs also work; their origin is validated against your authenticated host.
+
+### GET requests
+
+```bash
+# List organizations
+sentry api organizations/
+
+# Get a specific issue
+sentry api issues/123456789/
+```
+
+### POST requests
+
+```bash
+# Create a release
+sentry api organizations/my-org/releases/ \
+ -X POST -F version=1.0.0
+
+# With inline JSON body
+sentry api issues/123456789/ \
+ -X POST -d '{"status": "resolved"}'
+```
+
+### PUT requests
+
+```bash
+# Update an issue status
+sentry api issues/123456789/ \
+ -X PUT -F status=resolved
+
+# Assign an issue
+sentry api issues/123456789/ \
+ -X PUT --field assignedTo="user@example.com"
+```
+
+### DELETE requests
+
+```bash
+sentry api projects/my-org/my-project/ -X DELETE
+```
+
+### Advanced usage
+
+```bash
+# Add custom headers
+sentry api organizations/ -H "X-Custom: value"
+
+# Read body from a file
+sentry api projects/my-org/my-project/releases/ -X POST --input release.json
+
+# Verbose mode (shows full HTTP request/response)
+sentry api organizations/ --verbose
+
+# Preview the request without sending
+sentry api organizations/ --dry-run
+```
+
+### Dataset Names
+
+When querying the Events API (`/events/` endpoint), valid dataset values are: `spans`, `logs`, `errors`, `tracemetrics`, `profile_functions`, and `uptime_results`.
+
+### Binary responses
+
+Endpoints that return binary data — image attachments, minidumps, debug files — are streamed to stdout as raw bytes, so you can redirect them straight to a file:
+
+```bash
+sentry api "https://sentry.io/api/0/projects/my-org/my-project/events/EVENT_ID/attachments/ATTACHMENT_ID/?download=1" > screenshot.png
+```
+
+When the response is a PNG or JPEG image **and** you're on a graphics-capable terminal, the image is rendered inline instead of dumping raw bytes into your session. Terminals that speak the newer kitty graphics protocol (kitty, WezTerm, Ghostty, recent Konsole) are used in preference to sixel, which remains the fallback for older terminals. Redirecting or piping stdout always keeps the raw bytes. Set `SENTRY_NO_GRAPHICS=1` (or run `sentry cli defaults graphics off`) to disable inline rendering; `SENTRY_NO_SIXEL` is still honored as a deprecated alias.
+
+For full API documentation, see the [Sentry API Reference](https://docs.sentry.io/api/).
diff --git a/apps/cli-docs/src/fragments/commands/auth.md b/apps/cli-docs/src/fragments/commands/auth.md
new file mode 100644
index 000000000..bc2f3afaf
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/auth.md
@@ -0,0 +1,147 @@
+
+
+## Examples
+
+### OAuth login (recommended)
+
+```bash
+sentry auth
+```
+
+Bare `sentry auth` logs in when you're logged out and shows status when you're
+already authenticated. `sentry auth login` always starts the login flow.
+
+1. A URL and device code will be displayed
+2. Open the URL in your browser
+3. Enter the code when prompted
+4. Authorize the application
+5. The CLI stores the OAuth credentials and, when the server provides a refresh
+ token, automatically refreshes the access token
+
+### Token login
+
+```bash
+sentry auth --token YOUR_SENTRY_API_TOKEN
+```
+
+### Read-only OAuth login
+
+Request only read-only scopes — useful for tokens handed to AI agents or
+CI jobs that should not mutate Sentry state:
+
+```bash
+sentry auth --read-only
+```
+
+### Custom OAuth scopes
+
+Request specific scopes (repeatable, comma-separated):
+
+```bash
+sentry auth --scope project:read --scope org:read
+sentry auth --scope project:read,event:read
+```
+
+### Self-hosted Sentry
+
+Use `--url` (recommended) or the `SENTRY_URL` environment variable:
+
+```bash
+sentry auth --url https://sentry.example.com
+SENTRY_URL=https://sentry.example.com sentry auth
+```
+
+For token-based auth with self-hosted:
+
+```bash
+sentry auth --token YOUR_TOKEN --url https://sentry.example.com
+```
+
+See [Self-Hosted Sentry](../self-hosted/) for details.
+
+### Logout
+
+```bash
+sentry auth logout
+```
+
+### Refresh the OAuth access token
+
+```bash
+sentry auth refresh
+
+# Refresh with read-only scopes
+sentry auth refresh --read-only
+
+# Refresh with specific scopes
+sentry auth refresh --scope project:read --scope org:read
+```
+
+### Print stored token
+
+```bash
+sentry auth token
+```
+
+### Check auth status
+
+```bash
+sentry auth status
+```
+
+```
+✓ Authenticated
+User: username
+Access token expires: in 4 weeks
+Automatic refresh: enabled
+```
+
+```bash
+# Show the raw token
+sentry auth status --show-token
+
+# View current user
+sentry auth whoami
+```
+
+## Credential Storage
+
+Auth tokens are stored in the Sentry CLI configuration directory
+(`$XDG_CONFIG_HOME/sentry/`, defaulting to `~/.config/sentry/`, overridable
+with `SENTRY_CONFIG_DIR`) with restricted file permissions. A pre-existing
+legacy `~/.sentry/` directory is still honored.
+
+OAuth access tokens expire. When the server provides a refresh token, the CLI
+stores it and refreshes the access token automatically. Persist the
+configuration directory across runs to keep automatic refresh working. For
+ephemeral CI jobs or sandboxes that cannot persist stored credentials, provide
+an API token with `sentry auth login --token` or `SENTRY_AUTH_TOKEN`.
+
+## Token Precedence
+
+By default, the CLI checks for auth tokens in the following order:
+
+1. The stored credential from `sentry auth login`
+2. `SENTRY_AUTH_TOKEN` environment variable
+3. `SENTRY_TOKEN` environment variable (legacy alias)
+
+The stored credential takes priority. Stored OAuth credentials support
+automatic refresh; manually provided API tokens do not use a refresh token. To
+override this precedence and force environment tokens to win, set
+`SENTRY_FORCE_ENV_TOKEN=1`.
+
+When a token comes from an environment variable, the CLI skips expiry checks and automatic refresh.
+
+## Invalid Token Formatting
+
+Tokens must be a single line of printable ASCII characters, without spaces.
+When preparing an access token for storage or an authenticated request, the CLI
+removes surrounding whitespace and ASCII control characters, then rejects any
+remaining whitespace, control characters, and non-ASCII characters. It does not
+join split lines.
+
+If you see "Invalid authentication token", copy the complete token again into
+the configuration that supplies it. For environment tokens, check
+`SENTRY_AUTH_TOKEN` (or the legacy `SENTRY_TOKEN`). For stored credentials, run
+`sentry auth login` to replace them. A token rejected for formatting exits with
+code `12` (`AUTH_INVALID`).
diff --git a/apps/cli-docs/src/fragments/commands/build.md b/apps/cli-docs/src/fragments/commands/build.md
new file mode 100644
index 000000000..ffa76e914
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/build.md
@@ -0,0 +1,55 @@
+
+
+## Examples
+
+```bash
+# Upload an Android build (APK or AAB) for size analysis
+sentry build upload ./app-release.apk
+
+# Upload an iOS build (XCArchive directory or IPA)
+sentry build upload ./MyApp.xcarchive
+sentry build upload ./MyApp.ipa
+
+# Upload with a build configuration and release notes
+sentry build upload ./app.aab --build-configuration Release --release-notes "Nightly"
+
+# Tag a build with install groups (repeatable)
+sentry build upload ./app.aab --install-group qa --install-group beta
+
+# Attach explicit git metadata (otherwise auto-collected in CI)
+sentry build upload ./app.aab --head-sha "$GIT_SHA" --pr-number 42 --base-ref main
+
+# Download a build artifact by ID
+sentry build download 1234567890
+
+# Download to a specific path
+sentry build download 1234567890 --output ./app.ipa
+
+# Output the result as JSON
+sentry build download 1234567890 --json
+```
+
+## Important Notes
+
+- `build upload` supports **Android APK/AAB** and **iOS XCArchive/IPA**. An
+ XCArchive is a directory; an IPA is converted to an XCArchive layout for
+ upload. **Sentry SaaS only.**
+- iOS caveat: `Assets.car` asset catalogs are **not** parsed into per-asset
+ images (that required native macOS frameworks), so the server sees the raw
+ `.car` rather than a per-image breakdown. XCArchive symlinks and Unix file
+ permissions are preserved.
+- Multiple paths may be uploaded at once; the command exits non-zero if any
+ build fails to upload.
+- Git metadata (commit, branch, PR number, repo) is **auto-collected in CI**
+ (GitHub Actions env vars + the local git repo). Use `--no-git-metadata` to
+ disable it, `--force-git-metadata` to collect outside CI, or the explicit
+ `--head-sha` / `--base-sha` / `--head-ref` / `--base-ref` / `--pr-number` /
+ `--vcs-provider` / `--head-repo-name` / `--base-repo-name` flags to override.
+- `build download` fetches a mobile build artifact (APK or IPA) previously
+ uploaded to Sentry's preprod system for size analysis. **Sentry SaaS only.**
+- The build must be installable. Builds that are still processing — or that have
+ no downloadable artifact — cannot be downloaded.
+- Without `--output`, the artifact is saved as
+ `preprod_artifact_.` in the current directory.
+- The organization is resolved from `--org`, `SENTRY_ORG`, config defaults, or a
+ detected DSN.
diff --git a/apps/cli-docs/src/fragments/commands/cli.md b/apps/cli-docs/src/fragments/commands/cli.md
new file mode 100644
index 000000000..9053b9483
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/cli.md
@@ -0,0 +1,170 @@
+
+
+## Examples
+
+### Check for updates
+
+```bash
+sentry cli upgrade --check
+```
+
+```
+Installation method: curl
+Current version: 0.4.0
+Channel: stable
+Latest version: 0.5.0
+
+Run 'sentry cli upgrade' to update.
+```
+
+### Upgrade
+
+```bash
+# Upgrade to latest stable
+sentry cli upgrade
+
+# Upgrade to a specific version
+sentry cli upgrade 0.5.0
+
+# Force re-download
+sentry cli upgrade --force
+```
+
+### Release Channels
+
+```bash
+# Switch to nightly builds
+sentry cli upgrade nightly
+
+# Switch back to stable
+sentry cli upgrade stable
+```
+
+After switching, bare `sentry cli upgrade` will continue tracking that channel.
+
+| Channel | Description |
+|---------|-------------|
+| `stable` | Latest stable release (default) |
+| `nightly` | Built from `main`, updated on every commit |
+
+### Installation Detection
+
+The CLI detects how it was installed and uses the appropriate upgrade method:
+
+| Method | Detection |
+|--------|-----------|
+| curl | Binary installed via cli.sentry.dev (XDG: `~/.local/bin`; legacy: `~/.sentry/bin`) |
+| brew | Binary in a Homebrew Cellar (`brew install getsentry/tools/sentry`) |
+| npm | Globally installed via `npm install -g sentry` |
+| pnpm | Globally installed via `pnpm add -g sentry` |
+| bun | Globally installed via `bun install -g sentry` |
+
+Nightly builds are only available as standalone binaries (via the curl install method). Switching to nightly from a package manager install will automatically migrate to a standalone binary.
+
+### View and manage defaults
+
+```bash
+# Show all current defaults
+sentry cli defaults
+
+# Set default organization
+sentry cli defaults org my-org
+
+# Set default project
+sentry cli defaults project my-project
+
+# Set default Sentry URL (self-hosted)
+sentry cli defaults url https://sentry.example.com
+
+# Set custom HTTP headers (self-hosted, e.g. for IAP/proxies)
+sentry cli defaults headers "X-IAP: token"
+
+# Set a custom CA certificate (self-hosted, behind a TLS proxy)
+sentry cli defaults ca-cert /path/to/ca.pem
+
+# Disable telemetry
+sentry cli defaults telemetry off
+
+# Clear a single default
+sentry cli defaults org --clear
+
+# Clear all defaults
+sentry cli defaults --clear
+```
+
+### Import legacy settings
+
+Import settings from `.sentryclirc` files used by the legacy `sentry-cli`:
+
+```bash
+# Auto-detect and import .sentryclirc
+sentry cli import
+
+# Preview what would be imported
+sentry cli import --dry-run
+
+# Skip confirmation prompt
+sentry cli import --yes
+
+# Explicitly trust a self-hosted URL
+sentry cli import --url https://sentry.example.com
+
+# Skip API validation of the imported token
+sentry cli import --skip-validation
+```
+
+### Send feedback
+
+```bash
+# Send positive feedback
+sentry cli feedback i love this tool
+
+# Report an issue
+sentry cli feedback the issue view is confusing
+```
+
+Feedback is sent via Sentry's telemetry system. If telemetry is disabled (`SENTRY_CLI_NO_TELEMETRY=1`), feedback cannot be sent.
+
+### Fix configuration issues
+
+```bash
+sentry cli fix
+```
+
+### Print shell completions
+
+```bash
+# Print completions for your current shell (auto-detected from $SHELL)
+sentry cli completion
+
+# Generate for a specific shell
+sentry cli completion zsh > ~/.local/share/zsh/site-functions/_sentry
+eval "$(sentry cli completion bash)"
+sentry cli completion fish > ~/.config/fish/completions/sentry.fish
+```
+
+### Configure shell integration
+
+```bash
+# Run full setup (PATH, completions, agent skills)
+sentry cli setup
+
+# Skip agent skill installation
+sentry cli setup --no-agent-skills
+
+# Skip PATH and completion modifications
+sentry cli setup --no-modify-path --no-completions
+```
+
+### Uninstall
+
+```bash
+# Show what would be removed (dry run)
+sentry cli uninstall --dry-run
+
+# Uninstall, keeping config directory
+sentry cli uninstall --yes --keep-config
+
+# Full uninstall with confirmation
+sentry cli uninstall
+```
diff --git a/apps/cli-docs/src/fragments/commands/code-mappings.md b/apps/cli-docs/src/fragments/commands/code-mappings.md
new file mode 100644
index 000000000..8d47cb6f0
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/code-mappings.md
@@ -0,0 +1,36 @@
+
+
+
+## Examples
+
+```bash
+# Upload code mappings from a JSON file
+sentry code-mappings upload mappings.json
+
+# Specify repository explicitly
+sentry code-mappings upload mappings.json --repo owner/repo
+
+# Specify repository and default branch
+sentry code-mappings upload mappings.json --repo owner/repo --default-branch develop
+
+# Output as JSON
+sentry code-mappings upload mappings.json --json
+```
+
+## Input Format
+
+The JSON file must contain an array of objects with `stackRoot` and `sourceRoot`:
+
+```json
+[
+ { "stackRoot": "com/example/module", "sourceRoot": "src/main/java/com/example/module" },
+ { "stackRoot": "com/example/other", "sourceRoot": "src/main/java/com/example/other" }
+]
+```
+
+## Important Notes
+
+- Repository name and default branch are **auto-detected** from git remotes if
+ not provided via `--repo` and `--default-branch`.
+- Requires an Organization Token with `org:ci` scope.
+- Mappings are uploaded in batches of 300 per API request.
diff --git a/apps/cli-docs/src/fragments/commands/dart-symbol-map.md b/apps/cli-docs/src/fragments/commands/dart-symbol-map.md
new file mode 100644
index 000000000..9ff976fcb
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/dart-symbol-map.md
@@ -0,0 +1,22 @@
+
+
+## Examples
+
+```bash
+# Upload a dart symbol map with a debug ID
+sentry dart-symbol-map upload --debug-id 12345678-1234-1234-1234-123456789abc mapping.json
+
+# Validate without uploading
+sentry dart-symbol-map upload --debug-id 12345678-1234-1234-1234-123456789abc mapping.json --no-upload
+
+# Output as JSON
+sentry dart-symbol-map upload --debug-id 12345678-1234-1234-1234-123456789abc mapping.json --json
+```
+
+## Important Notes
+
+- The `--debug-id` flag is **required** — it associates the map with a native
+ debug file (dSYM/ELF). The sentry-dart-plugin extracts this automatically.
+- The mapping file must be a **JSON array of strings** with an even number of
+ entries (alternating obfuscated/original name pairs).
+- Supported on Sentry SaaS and self-hosted >= 25.8.0.
diff --git a/apps/cli-docs/src/fragments/commands/dashboard.md b/apps/cli-docs/src/fragments/commands/dashboard.md
new file mode 100644
index 000000000..6977cc957
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/dashboard.md
@@ -0,0 +1,157 @@
+
+
+## Examples
+
+### List dashboards
+
+```bash
+# List all dashboards
+sentry dashboard list
+
+# Filter by name pattern
+sentry dashboard list "Backend*"
+
+# Open dashboard list in browser
+sentry dashboard list -w
+```
+
+```
+ID TITLE WIDGETS CREATED
+12345 General 4 2024-01-15
+12346 Frontend Performance 6 2024-02-20
+12347 Backend Errors 3 2024-03-10
+```
+
+### View a dashboard
+
+```bash
+# View by title
+sentry dashboard view 'Frontend Performance'
+
+# View by ID
+sentry dashboard view 12345
+
+# Auto-refresh every 30 seconds
+sentry dashboard view "Backend Performance" --refresh 30
+
+# Open in browser
+sentry dashboard view 12345 -w
+```
+
+```
+Dashboard: Frontend Performance (ID: 12345)
+URL: https://my-org.sentry.io/dashboard/12345/
+
+Widgets:
+ #0 Error Count big_number count()
+ #1 Errors Over Time line count()
+ #2 Errors by Browser bar count() group by browser.name
+ #3 Top Endpoints table count(), p95(span.duration) group by transaction
+```
+
+### Create a dashboard
+
+```bash
+sentry dashboard create 'Frontend Performance'
+```
+
+```
+Created dashboard: Frontend Performance (ID: 12348)
+URL: https://my-org.sentry.io/dashboard/12348/
+```
+
+### Add widgets
+
+```bash
+# Simple counter widget
+sentry dashboard widget add 'My Dashboard' "Error Count" \
+ --display big_number --query count
+
+# Line chart with group-by
+sentry dashboard widget add 'My Dashboard' "Errors by Browser" \
+ --display line --query count --group-by browser.name
+
+# Table with multiple aggregates, sorted descending
+sentry dashboard widget add 'My Dashboard' "Top Endpoints" \
+ --display table \
+ --query count --query p95:span.duration \
+ --group-by transaction \
+ --sort -count --limit 10
+
+# With search filter
+sentry dashboard widget add 'My Dashboard' "Slow Requests" \
+ --display bar --query p95:span.duration \
+ --where "span.op:http.client" \
+ --group-by span.description
+```
+
+### Edit widgets
+
+```bash
+# Change display type
+sentry dashboard widget edit 12345 --title 'Error Count' --display bar
+
+# Rename a widget
+sentry dashboard widget edit 'My Dashboard' --index 0 --new-title 'Total Errors'
+
+# Change the query
+sentry dashboard widget edit 12345 --title 'Error Rate' --query p95:span.duration
+```
+
+### Delete widgets
+
+```bash
+# Delete by title
+sentry dashboard widget delete 'My Dashboard' --title 'Error Count'
+
+# Delete by index
+sentry dashboard widget delete 12345 --index 2
+```
+
+### View revision history
+
+```bash
+# List revisions by dashboard title
+sentry dashboard revisions 'Frontend Performance'
+
+# List revisions by dashboard ID
+sentry dashboard revisions 12345
+
+# With explicit org
+sentry dashboard revisions my-org 12345
+```
+
+### Restore a previous revision
+
+```bash
+# Restore by dashboard title and revision number
+sentry dashboard restore 'Frontend Performance' --revision 3
+
+# Restore by dashboard ID
+sentry dashboard restore 12345 --revision 1
+
+# With explicit org
+sentry dashboard restore my-org 12345 --revision 1
+```
+
+:::tip
+Use `sentry dashboard revisions` to find the revision number before restoring.
+:::
+
+## Query Shorthand
+
+The `--query` flag supports shorthand for aggregate functions:
+
+| Input | Expands to |
+|-------|-----------|
+| `count` | `count()` |
+| `p95:span.duration` | `p95(span.duration)` |
+| `avg:span.duration` | `avg(span.duration)` |
+| `count()` | `count()` (passthrough) |
+
+## Sort Shorthand
+
+| Input | Meaning |
+|-------|---------|
+| `count` | Sort by `count()` ascending |
+| `-count` | Sort by `count()` descending |
diff --git a/apps/cli-docs/src/fragments/commands/debug-files.md b/apps/cli-docs/src/fragments/commands/debug-files.md
new file mode 100644
index 000000000..979839c15
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/debug-files.md
@@ -0,0 +1,113 @@
+
+
+
+## Examples
+
+```bash
+# Inspect a debug information file (auto-detects the format)
+sentry debug-files check ./libexample.so
+sentry debug-files check MyApp.dSYM/Contents/Resources/DWARF/MyApp
+sentry debug-files check ./app.pdb --json
+
+# List the source files a debug file references (and whether they're available)
+sentry debug-files print-sources ./libexample.so
+sentry debug-files print-sources ./app.pdb --json
+
+# Locate debug files for one or more debug identifiers on disk
+sentry debug-files find
+sentry debug-files find --type dsym --path ./build
+sentry debug-files find --no-cwd --no-well-known -p /symbols --json
+
+# Bundle a debug file's referenced source files (run on the build machine)
+sentry debug-files bundle-sources ./libexample.so
+sentry debug-files bundle-sources ./app.pdb --output ./app.src.zip
+
+# Bundle JVM sources with a debug ID
+sentry debug-files bundle-jvm --output ./out --debug-id ./src
+
+# Exclude additional directories
+sentry debug-files bundle-jvm --output ./out --debug-id --exclude generated --exclude build-tools ./src
+
+# Output as JSON
+sentry debug-files bundle-jvm --output ./out --debug-id --json ./src
+
+# Upload debug information files (scans directories recursively)
+sentry debug-files upload ./build
+sentry debug-files upload ./libexample.so --include-sources
+
+# .zip archives are scanned in place; use --no-zips to skip them
+sentry debug-files upload ./symbols.zip
+sentry debug-files upload ./build --no-zips
+
+# Restrict by type or debug id, and wait for server-side processing
+sentry debug-files upload ./dsyms --type dsym --wait
+sentry debug-files upload ./build --id --require-all
+
+# Unity: upload IL2CPP line mappings (optionally with referenced C# sources)
+sentry debug-files upload ./build --il2cpp-mapping
+sentry debug-files upload ./build --il2cpp-mapping --include-sources
+
+# Preview what would be uploaded without uploading (no credentials needed)
+sentry debug-files upload ./build --no-upload
+```
+
+## Notes on `find`
+
+- `debug-files find` locates debug files **locally** by debug identifier — it
+ makes no API calls. It searches Xcode's `DerivedData` (for dSYMs, unless
+ `--no-well-known`), the current directory (unless `--no-cwd`), and any
+ `--path`/`-p` directories, recursively.
+- Restrict the search with `--type`/`-t` (repeatable): `dsym`, `elf`, `pe`,
+ `pdb`, `portablepdb`, `sourcebundle`, `breakpad`, `proguard`, `jvm`.
+- A debug identifier must match exactly, including any PE/PDB age suffix. A
+ Breakpad symbol file is listed when it matches, but does **not** satisfy the
+ request (the id is still reported as missing).
+- Exits non-zero if any requested identifier could not be located.
+
+## Important Notes
+
+- `check`, `print-sources`, `bundle-sources`, and `bundle-jvm` are **local-only**
+ — they make no network requests. They parse object files in-process
+ (Mach-O/dSYM, ELF, PE/PDB, Portable PDB, WebAssembly, Breakpad, source bundles)
+ via a bundled `symbolic` WASM module.
+- `check` exits non-zero if the file is not usable for symbolication (no debug
+ id or no useful features).
+- `print-sources` lists the source files each object references, reporting for
+ each whether the source is embedded in the debug file, available via a source
+ link, or present on the local disk. It is a read-only preview of what
+ `bundle-sources` would collect and always exits zero on a parseable file.
+- `bundle-sources` reads source files from the paths recorded in the debug info,
+ so it is normally run on the build machine right after compiling. Referenced
+ files that are not present locally are skipped; it exits non-zero (writing
+ nothing) when none are found. The bundle defaults to `.src.zip` and is
+ uploaded via `sentry debug-files upload`.
+- `upload` scans each path (files or directories, walked recursively) for
+ native debug information files, parses them in-process, and uploads matching
+ files via the chunk-upload protocol. Use `--type`/`--id` to restrict which
+ files are sent, `--no-debug`/`--no-unwind`/`--no-sources` to drop files whose
+ only useful feature is the named one, and `--include-sources` to attach a
+ source bundle per file. `.zip` archives are scanned in place by default (their
+ entries run through the same filters; nested archives are not recursed) — pass
+ `--no-zips` to skip them. `--derived-data` additionally scans Xcode's
+ `~/Library/Developer/Xcode/DerivedData` folder (macOS only). `--no-upload`
+ previews the selection without credentials; `--wait`/`--wait-for` block on
+ server-side processing and exit non-zero if any file fails. `--require-all`
+ fails if a requested `--id` was not found. The server-advertised maximum file
+ size and maximum processing wait are honored automatically (oversized files
+ are skipped with a warning). BCSymbolMap resolution (the legacy
+ `--symbol-maps` flag) is intentionally unsupported — it only applies to Apple
+ Bitcode, which Apple has deprecated and the App Store no longer accepts. Use
+ the legacy Rust `sentry-cli` if you still need it.
+- Managed .NET PE assemblies that embed a Portable PDB have it extracted and
+ uploaded automatically as a separate `.pdb` debug file (no flag needed).
+- `--il2cpp-mapping` computes Unity IL2CPP C++→C# line mappings from each file's
+ referenced generated C++ sources and uploads them as separate `il2cpp` debug
+ files. Combine with `--include-sources` to also bundle the referenced C#
+ source files.
+- Upload a JVM bundle separately via `sentry debug-files upload --type jvm`.
+- Supported JVM source file extensions: `.java`, `.kt`, `.scala`, `.sc`,
+ `.groovy`, `.gvy`, `.gy`, `.gsh`, `.clj`, `.cljc`
+- Build output directories (`build/`, `target/`, `out/`, `bin/`) are
+ automatically excluded unless they appear under a `src/` ancestor.
+- Source-set prefixes (e.g., `src/main/java/`) are stripped to produce
+ package-relative paths matching JVM stack traces.
diff --git a/apps/cli-docs/src/fragments/commands/docs.md b/apps/cli-docs/src/fragments/commands/docs.md
new file mode 100644
index 000000000..c1b073749
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/docs.md
@@ -0,0 +1,24 @@
+## Examples
+
+### Ask a documentation question
+
+```bash
+sentry docs "How do I configure tracing in Next.js?"
+```
+
+The answer includes inline links and a Sources section containing the Sentry
+documentation pages used to answer the question.
+
+### Search the documentation index
+
+```bash
+sentry docs list "source maps"
+```
+
+```
+TITLE DESCRIPTION URL
+Source Maps Upload source maps for JavaScript https://docs.sentry.io/...
+```
+
+Use `--limit` to restrict the number of results or `--json` for structured
+output.
diff --git a/apps/cli-docs/src/fragments/commands/event.md b/apps/cli-docs/src/fragments/commands/event.md
new file mode 100644
index 000000000..1cb7e39fb
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/event.md
@@ -0,0 +1,146 @@
+
+
+
+## Examples
+
+### Sending Events
+
+```bash
+# Send an error event (default level)
+sentry event send -m "Something went wrong"
+
+# Specify level, release, and environment
+sentry event send -m "Deploy check" -l info -r 1.0.0 -E production
+
+# Add tags and extra data
+sentry event send -m "Payment failed" --tag env:prod --tag region:us-east --extra amount:99.99
+
+# Set user context
+sentry event send -m "Login error" --user id:42 --user email:alice@example.com
+
+# Custom fingerprint to group related events together
+sentry event send -m "DB timeout" --fingerprint db-timeout --fingerprint {{ default }}
+```
+
+### Send from a JSON file
+
+```bash
+# Send a serialized Sentry Event object
+sentry event send ./crash.json
+
+# Send without re-parsing (raw mode — also supports pre-built envelopes)
+sentry event send --raw ./crash.json
+sentry event send --raw ./captured.envelope
+```
+
+Prefix relative file paths with `./` to distinguish them from a project target.
+
+### DSN authentication
+
+`sentry event send` authenticates with ingest via a **DSN**, not a user token.
+An explicit DSN, `SENTRY_DSN`, and project auto-detection do not require login.
+Passing `` or `/` does: the CLI uses your session to
+resolve the project and fetch its active DSN, then sends to ingest. If a project
+has multiple active DSNs, pass the desired DSN explicitly.
+
+The DSN is resolved in priority order:
+
+1. A leading ``, ``, or `/` positional
+2. `SENTRY_DSN` environment variable
+3. Auto-detection from the current project (`.env`, source code, env files)
+
+```bash
+# Explicit DSN
+sentry event send "https://key@o123.ingest.us.sentry.io/456" -m "Test"
+
+# Via environment variable
+export SENTRY_DSN="https://key@o123.ingest.us.sentry.io/456"
+sentry event send -m "Test"
+
+# Project target (logged-in session; CLI fetches its sole active DSN)
+sentry event send cli -m "Test"
+
+# Org/project target
+sentry event send sentry/cli -m "Test"
+
+# Auto-detect from the current project
+sentry event send -m "Test"
+```
+
+### Listing Events
+
+```bash
+# List events for an issue (using short ID)
+sentry event list PROJ-ABC
+
+# List events for an issue (using numeric ID)
+sentry event list 123456789
+
+# Filter by search query
+sentry event list PROJ-ABC --query "browser:Chrome"
+
+# Include full event bodies (stacktraces)
+sentry event list PROJ-ABC --full
+
+# Limit results and time range
+sentry event list PROJ-ABC --limit 50 --period 24h
+
+# Paginate through results
+sentry event list PROJ-ABC -c next
+sentry event list PROJ-ABC -c prev
+
+# Output as JSON
+sentry event list PROJ-ABC --json
+```
+
+### Viewing Events
+
+```bash
+sentry event view abc123def456abc123def456abc12345
+```
+
+```
+Event: abc123def456abc123def456abc12345
+Issue: FRONT-ABC
+Timestamp: 2024-01-20 14:22:00
+
+Exception:
+ TypeError: Cannot read property 'foo' of undefined
+ at processData (app.js:123:45)
+ at handleClick (app.js:89:12)
+ at HTMLButtonElement.onclick (app.js:45:8)
+
+Tags:
+ browser: Chrome 120
+ os: Windows 10
+ environment: production
+ release: 1.2.3
+
+Context:
+ url: https://example.com/app
+ user_id: 12345
+```
+
+```bash
+# Open in browser
+sentry event view abc123def456abc123def456abc12345 -w
+
+# Download an attachment listed by `sentry event view --json`
+sentry api "https://sentry.io/api/0/projects/my-org/my-project/events/EVENT_ID/attachments/ATTACHMENT_ID/?download=1" > screenshot.png
+```
+
+## Finding Event IDs
+
+Event IDs can be found:
+
+1. In the Sentry UI when viewing an issue's events
+2. In the output of `sentry issue view` commands
+3. In error reports sent to Sentry (as `event_id`)
+
+## Backward compatibility
+
+The old sentry-cli top-level command is available as a hidden alias:
+
+```bash
+sentry send-event # same as: sentry event send
+```
diff --git a/apps/cli-docs/src/fragments/commands/explore.md b/apps/cli-docs/src/fragments/commands/explore.md
new file mode 100644
index 000000000..70adfa6c9
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/explore.md
@@ -0,0 +1,116 @@
+
+
+## Examples
+
+### Top errors (default)
+
+```bash
+# Top errors in the last 24 hours, scoped to a project
+sentry explore my-org/cli
+
+# All projects in an org
+sentry explore my-org/
+
+# Bare project slug (searches across orgs)
+sentry explore cli
+
+# Auto-detect from DSN/config
+sentry explore
+```
+
+### Spike analysis
+
+```bash
+# Errors with user impact for a specific UTC window
+sentry explore my-org/cli -F title -F "count()" -F "count_unique(user)" \
+ --period "2024-01-15T00:00:00Z/2024-01-16T00:00:00Z"
+
+# Filter by specific error type (combines with auto-injected project filter)
+sentry explore my-org/cli -F title -F "count()" \
+ -q "error.type:TypeError" --period 1h
+```
+
+### Span queries (performance)
+
+```bash
+# Span operation latency by route
+sentry explore my-org/cli -F span.op -F "p50(span.duration)" \
+ -F "p95(span.duration)" --dataset spans --period 1h
+
+# Top spans by count
+sentry explore my-org/cli -F span.op -F "count()" \
+ --dataset spans --sort "-count()"
+```
+
+### Metrics
+
+Use `--metric` (`-m`) to query metrics by name. The CLI auto-resolves the metric's type and unit.
+
+```bash
+# Sum a custom metric (e.g., LLM token usage) across an org
+sentry explore my-org/ -m llm.token_usage --dataset metrics --period 7d
+
+# Break down by a tag column (e.g., model name)
+sentry explore my-org/seer -F gen_ai.request.model \
+ -m llm.token_usage --dataset metrics --period 7d
+
+# Use a different aggregation (default is sum)
+sentry explore my-org/ -m cache.hit_rate --agg avg --dataset metrics
+```
+
+You can also use the raw tracemetrics format: `aggregation(value,metric_name,metric_type,unit)`.
+
+```bash
+sentry explore my-org/ \
+ -F "sum(value,llm.token_usage,distribution,none)" \
+ --dataset metrics --period 7d
+```
+
+### Replays
+
+```bash
+# List recent replays
+sentry explore my-org/cli --dataset replays --period 24h
+
+# Filter replays by activity level
+sentry explore my-org/cli --dataset replays -F activity -F duration \
+ -F "count_errors" --period 7d
+```
+
+### Logs
+
+```bash
+# Log severity counts in the last hour
+sentry explore my-org/cli -F severity -F "count()" \
+ --dataset logs --period 1h
+```
+
+### JSON output for scripting
+
+```bash
+# Pipe to jq for filtering
+sentry explore my-org/cli -F title -F "count()" --json | jq '.data[:5]'
+
+# Get raw data for analysis
+sentry explore my-org/cli -F title -F "count()" -F "count_unique(user)" \
+ --json --limit 100
+```
+
+## Datasets
+
+| Dataset | Description |
+|---------|-------------|
+| `errors` | Error events (default) |
+| `spans` | Span/transaction data for performance analysis |
+| `metrics` | Custom and built-in metrics |
+| `logs` | Structured log entries |
+| `replays` | Session replay recordings |
+
+## Target Patterns
+
+| Target | Behavior |
+|--------|----------|
+| `/` | Auto-adds `project:` to query |
+| `/` | All projects in org (no project filter) |
+| `` | Searches for project across all accessible orgs |
+| (omitted) | Auto-detect from DSN/config |
diff --git a/apps/cli-docs/src/fragments/commands/feedback.md b/apps/cli-docs/src/fragments/commands/feedback.md
new file mode 100644
index 000000000..b98ffb417
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/feedback.md
@@ -0,0 +1,62 @@
+## Examples
+
+### List User Feedback
+
+Modern User Feedback is stored as Feedback issues. The command always limits
+issue searches to `issue.category:feedback`; it does not use the legacy User
+Reports API.
+
+```bash
+# Auto-detect the organization from the current project
+sentry feedback list
+
+# List Feedback for one project
+sentry feedback list my-org/frontend
+
+# List Feedback across every project in an organization
+sentry feedback list my-org/
+
+# Search for a project across accessible organizations
+sentry feedback list frontend
+```
+
+The unresolved inbox from the last 14 days is shown by default. Select another
+mailbox or expand the time range with flags:
+
+```bash
+sentry feedback list my-org/frontend --status resolved
+sentry feedback list my-org/frontend --status spam
+sentry feedback list my-org/frontend --status all --period 90d
+sentry feedback list my-org/frontend --query "message:*checkout*"
+```
+
+Use `--json` for the standard paginated envelope. Navigate pages in either
+direction with `--cursor next` and `--cursor prev`.
+
+### View User Feedback
+
+```bash
+# Most recent unresolved Feedback, with detected or explicit organization
+sentry feedback view @latest
+sentry feedback view my-org/@latest
+
+# Short ID or numeric ID
+sentry feedback view FRONTEND-2SDJ
+sentry feedback view 5146636313
+
+# Explicit organization
+sentry feedback view my-org/FRONTEND-2SDJ
+
+# `view` is the default command; `show` is an alias
+sentry feedback my-org/FRONTEND-2SDJ
+sentry feedback show my-org/FRONTEND-2SDJ
+
+# Open the Feedback item in Sentry
+sentry feedback view my-org/FRONTEND-2SDJ --web
+```
+
+`@latest` selects the most recently active unresolved Feedback.
+
+The detail view includes the complete message and, when available, its latest
+event, linked error, Session Replays, and attachment metadata. If the supplied
+ID belongs to another issue category, use `sentry issue view` instead.
diff --git a/apps/cli-docs/src/fragments/commands/index.md b/apps/cli-docs/src/fragments/commands/index.md
new file mode 100644
index 000000000..d1ab7faf7
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/index.md
@@ -0,0 +1,26 @@
+
+
+## Global Options
+
+All commands support the following global options:
+
+- `--help` - Show help for the command
+- `--version` - Show CLI version
+- `--log-level ` - Set log verbosity (`error`, `warn`, `log`, `info`, `debug`, `trace`). Overrides `SENTRY_LOG_LEVEL`
+- `--verbose` - Shorthand for `--log-level debug`
+
+## JSON Output
+
+Most list and view commands support `--json` flag for JSON output, making it easy to integrate with other tools:
+
+```bash
+sentry org list --json | jq '.[] | .slug'
+```
+
+## Opening in Browser
+
+View commands support `-w` or `--web` flag to open the resource in your browser:
+
+```bash
+sentry issue view PROJ-123 -w
+```
diff --git a/apps/cli-docs/src/fragments/commands/info.md b/apps/cli-docs/src/fragments/commands/info.md
new file mode 100644
index 000000000..de341e208
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/info.md
@@ -0,0 +1,25 @@
+## Examples
+
+```bash
+# Print the resolved config and verify authentication
+sentry info
+
+# Verify only authentication (don't require a default org/project)
+sentry info --no-defaults
+
+# Machine-readable status for external tooling (always exits 0)
+sentry info --config-status-json
+```
+
+## Important Notes
+
+- `info` prints the resolved Sentry server URL and the default organization and
+ project, then verifies your credentials against the server.
+- The server URL comes from `SENTRY_URL`, the stored default, or the SaaS
+ default; org/project come from `SENTRY_ORG`/`SENTRY_PROJECT` or stored
+ defaults. `have_dsn` reflects whether `SENTRY_DSN` is set.
+- Exits non-zero when authentication fails, or (unless `--no-defaults`) when no
+ default organization/project is configured.
+- `--config-status-json` emits a JSON status object (`config`, `auth`,
+ `have_dsn`) for external tooling and **always exits 0** — it is a status
+ report, not a check. For general machine-readable output use `--json`.
diff --git a/apps/cli-docs/src/fragments/commands/init.md b/apps/cli-docs/src/fragments/commands/init.md
new file mode 100644
index 000000000..12cf0eff5
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/init.md
@@ -0,0 +1,70 @@
+
+
+> **Experimental:** `sentry init` is experimental and may modify your source files. Always review changes before committing.
+
+**Authentication:** Interactive runs start the OAuth login flow automatically when credentials are missing or the stored session has expired. Non-interactive and CI runs must authenticate before running `sentry init`.
+
+## Examples
+
+```bash
+# Interactive setup
+sentry init
+
+# Non-interactive agent/CI setup
+sentry init --yes --features errors,tracing,replay
+
+# Dry run to preview changes
+sentry init --dry-run
+
+# Target a subdirectory
+sentry init ./my-app
+
+# Use a specific org (auto-detect project)
+sentry init acme/
+
+# Use a specific org and project
+sentry init acme/my-app
+
+# Assign a team when creating a new project
+sentry init acme/ --team backend
+
+# Enable specific features
+sentry init --features profiling,replay
+```
+
+## Target Syntax
+
+| Syntax | Meaning |
+|--------|---------|
+| _(omitted)_ | Auto-detect org and project |
+| `acme/` | Use org `acme`, auto-detect or create project |
+| `acme/my-app` | Use org `acme` and project `my-app` |
+| `my-app` | Search for project `my-app` across all accessible orgs |
+
+Path-like arguments (starting with `.`, `/`, or `~`) are always treated as the directory. The order of target and directory can be swapped — the CLI will auto-correct with a warning.
+
+## Available Features
+
+| Feature | Description |
+|---------|-------------|
+| `errors` | Error monitoring |
+| `tracing` | Performance tracing |
+| `logs` | Log integration |
+| `replay` | Session replay |
+| `profiling` | Profiling |
+| `crons` | Cron job monitoring |
+| `agent-tracing` | Agent tracing for AI/LLM apps |
+| `mcp-observability` | MCP (Model Context Protocol) observability |
+
+## What the Wizard Does
+
+1. **Detects your framework** — scans your project files to identify the platform and framework
+2. **Installs the SDK** — adds the appropriate Sentry SDK package to your project
+3. **Instruments your code** — configures error monitoring, tracing, and any selected features
+
+### Supported Platforms
+
+- **JavaScript / TypeScript** — Next.js, Express, SvelteKit, React
+- **Python** — Flask, FastAPI
+
+More platforms and frameworks are coming soon.
diff --git a/apps/cli-docs/src/fragments/commands/issue.md b/apps/cli-docs/src/fragments/commands/issue.md
new file mode 100644
index 000000000..531e2c2be
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/issue.md
@@ -0,0 +1,320 @@
+
+
+## Examples
+
+### List issues
+
+```bash
+# List issues in a specific project
+sentry issue list my-org/frontend
+
+# All projects in an org
+sentry issue list my-org/
+
+# Search for a project across organizations
+sentry issue list frontend
+```
+
+```
+ID SHORT ID TITLE COUNT USERS
+123456789 FRONT-ABC TypeError: Cannot read prop... 1.2k 234
+987654321 FRONT-DEF ReferenceError: x is not de... 456 89
+```
+
+**Filter by status and search:**
+
+```bash
+# Show only unresolved issues
+sentry issue list my-org/frontend --query "is:unresolved"
+
+# Show resolved issues
+sentry issue list my-org/frontend --query "is:resolved"
+
+# Sort by frequency
+sentry issue list my-org/frontend --sort freq --limit 20
+
+# Sort by Sentry's "recommended" relevance ranking (the default on sentry.io;
+# self-hosted instances default to "date" and require a recent Sentry version
+# to accept --sort recommended)
+sentry issue list my-org/frontend --sort recommended
+
+# Multiple filters (space-separated = implicit AND)
+sentry issue list --query "is:unresolved level:error assigned:me"
+
+# Negation and wildcards
+sentry issue list --query "!browser:Chrome message:*timeout*"
+
+# Match multiple values for one key (in-list syntax)
+sentry issue list --query "browser:[Chrome,Firefox]"
+```
+
+:::caution[Search syntax]
+Sentry search uses **implicit AND** — space-separated terms are all required.
+**AND/OR operators are not supported** for issue search. Use alternatives:
+- `key:[val1,val2]` — in-list syntax (matches val1 OR val2 for one key)
+- Run separate queries for different terms
+- `*term*` — wildcard matching
+
+Full syntax reference: [Sentry Search Docs](https://docs.sentry.io/concepts/search/)
+:::
+
+### Magic selectors
+
+Use `@latest` and `@most_frequent` to target issues without knowing their ID:
+
+```bash
+# View the most recent issue
+sentry issue view @latest
+
+# Explain the most frequently occurring issue
+sentry issue explain @most_frequent
+
+# Generate a fix plan for the latest issue
+sentry issue plan @latest
+```
+
+### Common mistakes
+
+- **Don't use the issue command name as an issue prefix** — `sentry issue explain issue-1` is parsed as project `issue` + suffix `1`. Use a real project prefix: `sentry issue explain FRONT-1` or a numeric ID: `sentry issue explain 123456789`.
+- **Issue short IDs use uppercase prefixes** — `CLI-G`, not `cli-g`. The CLI tolerates lowercase input in most commands, but agents should prefer the canonical form.
+- **`org/project` targets use slugs, not numeric IDs** — `sentry issue list my-org/6775615880` fails when `6775615880` is a project ID copied from a URL. Run `sentry project list my-org/` to find the slug (e.g. `frontend`).
+- **Prefer auto-detect over guessing slugs** — omit the target when possible; the CLI resolves org/project from DSNs, `.env` files, and your working directory.
+
+### List events for an issue
+
+```bash
+# List recent events for an issue
+sentry issue events FRONT-ABC
+
+# Filter events by search query
+sentry issue events FRONT-ABC --query "browser:Chrome"
+
+# Show full event details
+sentry issue events FRONT-ABC --full
+
+# Limit results and filter by time period
+sentry issue events FRONT-ABC --limit 50 --period 24h
+
+# Paginate through results
+sentry issue events FRONT-ABC -c next
+```
+
+### View an issue
+
+```bash
+sentry issue view FRONT-ABC
+
+# Multiple issues in one invocation (space-separated, not commas)
+sentry issue view FRONT-ABC BACK-2
+```
+
+```
+Issue: TypeError: Cannot read property 'foo' of undefined
+Short ID: FRONT-ABC
+Status: unresolved
+First seen: 2024-01-15 10:30:00
+Last seen: 2024-01-20 14:22:00
+Events: 1,234
+Users affected: 234
+
+Latest event:
+ Browser: Chrome 120
+ OS: Windows 10
+ URL: https://example.com/app
+```
+
+```bash
+# Open one or more issues in the browser (up to 5 tabs by default)
+sentry issue view FRONT-ABC BACK-2 -w
+
+# Explicitly allow more than 5 tabs
+sentry issue view FRONT-ABC BACK-2 API-3 WEB-4 IOS-5 OPS-6 -w --force
+```
+
+```bash
+# GitHub-style identifiers work too (the "#" replaces the final slash)
+sentry issue view my-org/my-project#FRONT-ABC
+sentry issue view my-project#FRONT-ABC
+```
+
+**JSON output for agents and scripting:**
+
+`--json` returns the issue fields at the top level plus the latest event under
+`event`, the resolved `org` slug, related `replayIds`, and `trace` context.
+Prefer this over the human output when parsing programmatically. One issue ID
+still returns a single object; multiple IDs return an array of those objects.
+
+```bash
+# Full JSON (issue fields + latest event + trace/replay context)
+sentry issue view FRONT-ABC --json
+
+# Multiple issues: JSON is an array of the same objects
+sentry issue view FRONT-ABC BACK-2 --json
+
+# Select specific top-level fields to keep output small
+sentry issue view FRONT-ABC --json --fields shortId,title,culprit,count,userCount,permalink
+
+# Pull named fields off the latest event instead of the whole `event` object —
+# the event's `request` entry can include live session data (cookies, headers,
+# body), so extract only what you need
+sentry issue view FRONT-ABC --json --fields event.id,event.title,event.dateCreated
+```
+
+Common `jq` shapes for the `--json` output. The latest event's data lives under
+`event.entries[]`, each tagged with a `type` (`"exception"`, `"request"`,
+`"breadcrumbs"`, …) and a `data` payload — not as top-level `event.request` /
+`event.exception` keys:
+
+```bash
+# Issue summary
+sentry issue view FRONT-ABC --json | jq '{shortId, title, count, userCount, permalink}'
+
+# Latest event id + culprit
+sentry issue view FRONT-ABC --json | jq '{event: .event.id, culprit}'
+
+# Just the request URL and method (avoids the full request/session blob)
+sentry issue view FRONT-ABC --json | jq '.event.entries[] | select(.type == "request") | .data | {url, method}'
+
+# Exception type and value from the latest event
+sentry issue view FRONT-ABC --json | jq '.event.entries[] | select(.type == "exception") | .data.values[0] | {type, value}'
+```
+
+### Explain issues with Seer AI
+
+```bash
+# Analyze root cause (may take a few minutes for new issues)
+sentry issue explain 123456789
+
+# By short ID with org prefix
+sentry issue explain my-org/MYPROJECT-ABC
+
+# Analyze multiple issues in one invocation
+sentry issue explain FRONT-ABC BACK-2
+
+# Force a fresh analysis
+sentry issue explain 123456789 --force
+```
+
+With `--json`, one explained issue preserves the existing array of root causes.
+Multiple issues return an array of labeled objects containing `issue`, `org`,
+`issueId`, and `rootCauses`.
+
+### Generate a plan with Seer AI
+
+```bash
+# Generate a fix plan (automatically runs explain if needed)
+sentry issue plan 123456789
+
+# Force a fresh plan even if one already exists
+sentry issue plan 123456789 --force
+```
+
+**Requirements:**
+
+- Seer AI enabled for your organization
+- GitHub integration configured with repository access
+- Code mappings set up to link stack frames to source files
+- Root cause analysis is run automatically if needed (the `plan` command triggers `explain` first)
+
+### Resolve and reopen issues
+
+```bash
+# Resolve immediately (no regression tracking)
+sentry issue resolve CLI-G5
+
+# Resolve in a specific release — future events on newer releases are
+# regression-flagged
+sentry issue resolve CLI-G5 --in 0.26.1
+
+# Monorepo-style releases work too (no special parsing)
+sentry issue resolve CLI-G5 --in spotlight@1.2.3
+
+# Resolve in the next release (tied to current HEAD)
+sentry issue resolve CLI-G5 --in @next
+sentry issue resolve CLI-G5 -i @next
+
+# Resolve in the current git HEAD — auto-detects the Sentry repo from
+# your git origin remote (hard-errors if it can't)
+sentry issue resolve CLI-G5 --in @commit
+
+# Explicit commit + repo (no git inspection; repo must be registered in Sentry)
+sentry issue resolve CLI-G5 --in @commit:getsentry/cli@abc123def
+
+# Reopen a resolved issue
+sentry issue unresolve CLI-G5
+sentry issue reopen CLI-G5 # alias
+```
+
+:::note[How `@commit` auto-detects]
+`--in @commit` reads `HEAD` and the `origin` remote, parses the remote as
+`owner/repo`, then looks it up in your org's Sentry repositories (cached
+locally for 7 days). If any step fails, the command stops with a clear
+error pointing you at `--in @commit:@` or `sentry repo list /`
+— no silent fallback to a different resolution mode.
+:::
+
+### Merge fragmented issues
+
+Consolidate multiple issues (e.g. same logical error split by Sentry's
+default stack-trace grouping) into a single canonical group:
+
+```bash
+# Let Sentry auto-pick the parent (typically the largest by event count)
+sentry issue merge CLI-K9 CLI-15H CLI-15N
+
+# Pin the canonical parent explicitly — accepts the same formats as
+# positional args, including org-qualified and project-alias forms
+sentry issue merge CLI-K9 CLI-15H CLI-15N --into CLI-K9
+sentry issue merge my-org/CLI-K9 my-org/CLI-15H --into my-org/CLI-K9
+sentry issue merge cli-k9 cli-15h --into cli-k9 # alias form
+
+# Cross-org merges are rejected — all issues must share an organization
+# Non-error issue types (performance, info, etc.) cannot be merged
+```
+
+### Archive and ignore issues
+
+Archive an issue to suppress alerts. Without `--until`, the issue is archived
+forever. Use `--until` to set a condition for automatic unarchival:
+
+```bash
+# Archive forever (fully silenced)
+sentry issue archive CLI-G5
+
+# Smart detection — unarchives when Sentry detects a spike in event frequency
+sentry issue archive CLI-G5 --until auto
+
+# Duration-based
+sentry issue archive CLI-G5 --until 1h # 1 hour
+sentry issue archive CLI-G5 --until 7d # 7 days
+sentry issue archive CLI-G5 --until 2026-12-31 # specific date
+
+# Count-based — unarchive after N more events
+sentry issue archive CLI-G5 --until 100x
+
+# User-based — unarchive after N more users affected
+sentry issue archive CLI-G5 --until 10u
+
+# Compound — count within a time window
+sentry issue archive CLI-G5 --until 100x/1h # 100 events within 1 hour
+sentry issue archive CLI-G5 --until 10u/1d # 10 users within 1 day
+
+# Verbose forms also work
+sentry issue archive CLI-G5 --until 10events/2hours
+
+# 'ignore' is an alias for 'archive'
+sentry issue ignore CLI-G5 --until auto
+```
+
+:::tip[`--until` syntax reference]
+| Format | Meaning |
+|--------|---------|
+| `auto` | Unarchive on event frequency spike (recommended) |
+| `30m`, `1h`, `7d`, `1w` | Duration (minutes, hours, days, weeks) |
+| `2026-05-15` | Absolute date (computed as time delta) |
+| `10x` or `10events` | After 10 more events |
+| `10u` or `10users` | After 10 more users affected |
+| `10x/5m` | 10 events within 5 minutes |
+| `10users/2hours` | 10 users within 2 hours |
+| *(omitted)* | Archive forever |
+:::
diff --git a/apps/cli-docs/src/fragments/commands/local.md b/apps/cli-docs/src/fragments/commands/local.md
new file mode 100644
index 000000000..92a571f8f
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/local.md
@@ -0,0 +1,187 @@
+
+
+`sentry local` runs a local development server that captures Sentry SDK envelopes from your dev stack and surfaces errors, traces, and logs in real time — right in your terminal. No authentication required.
+
+No DSN is required either. If your app has no DSN configured, events flow **only** to the local server — nothing reaches your Sentry organization and no production quota is used. If a DSN *is* set, the SDK sends to both Sentry and the local server.
+
+If a server is already running on the port, the command attaches as an SSE consumer instead of starting a duplicate.
+
+## Examples
+
+```bash
+# Start the server and tail events (default)
+sentry local
+
+# Run your app with the local server auto-enabled
+sentry local run -- npm run dev
+sentry local run -- python manage.py runserver
+
+# Use a custom port
+sentry local --port 9000
+
+# Only show errors and logs (filter out transactions)
+sentry local -f error -f log
+
+# Run quietly (suppress per-envelope tail output)
+sentry local --quiet
+```
+
+## `sentry local run`
+
+Runs a command with `SENTRY_SPOTLIGHT` injected into the environment. The Sentry SDK automatically detects this variable and sends envelopes to the local server. No code changes needed.
+
+If nothing is listening on the port, a server is started in the background and shut down when your command exits. If something already is — the Spotlight desktop app's own sidecar, or a `sentry local serve` in another terminal — the command attaches to it as an SSE consumer, so events still tail to your terminal either way.
+
+Env vars injected into the child process:
+
+| Variable | Value |
+|----------|-------|
+| `SENTRY_SPOTLIGHT` | `http://localhost:/stream` |
+| `SENTRY_SPOTLIGHT` | `http://localhost:/stream` |
+| `SENTRY_TRACES_SAMPLE_RATE` | `1` (unless already set) |
+| `SENTRY_RELEASE` | `sentry-cli-local` (unless already set) |
+
+The `` variants cover every common framework client prefix so the spotlight URL is inlined into your browser bundle no matter which bundler you use: `PUBLIC_` (SvelteKit, Astro, Qwik), `NEXT_PUBLIC_` (Next.js), `VITE_` (Vite), `NUXT_PUBLIC_` (Nuxt), `REACT_APP_` (Create React App), `VUE_APP_` (Vue CLI), and `GATSBY_` (Gatsby).
+
+**Server vs. client.** Server-side SDKs (`@sentry/node`, Python, and friends) read `SENTRY_SPOTLIGHT` automatically — no code changes needed.
+
+**Cloudflare Workers.** Wrangler does not expose inherited process environment variables to Worker code. When `local run` detects `wrangler dev` together with a `wrangler.json`, `wrangler.jsonc`, or `wrangler.toml`, it automatically adds `--var SENTRY_SPOTLIGHT:http://localhost:/stream`.
+
+This creates an ephemeral Worker binding that `@sentry/cloudflare` reads from its `env` object, so you do not need `spotlight: true` in `withSentry()` and no project file is modified. An explicit `--var SENTRY_SPOTLIGHT:...` is preserved.
+
+For browser/client events, the CLI exposes the spotlight URL under every framework client prefix above. Once the [browser SDK reads these variables automatically](https://github.com/getsentry/sentry-javascript/pull/18198), client-side capture will be zero-config too. **Until then**, reference the variable matching your framework in your client config:
+
+```ts
+// Next.js example — other frameworks use their own env access pattern
+// (e.g. import.meta.env.VITE_SENTRY_SPOTLIGHT for Vite-based frameworks).
+Sentry.init({ spotlight: process.env.NEXT_PUBLIC_SENTRY_SPOTLIGHT ?? false });
+```
+
+## Browser UI
+
+Use `--open` to launch the Sentry Local UI ([local.sentry.dev](https://local.sentry.dev)) in your browser. The UI connects to the local receiver via the loopback stream endpoint and provides a visual workspace for browsing errors, traces, logs, and AI spans captured during the session.
+
+```bash
+# Start the server and open the UI
+sentry local --open
+
+# Run your app with the UI
+sentry local run --open -- npm run dev
+```
+
+The `--open` flag requires a loopback `--host` (localhost, 127.0.0.1, or ::1). The UI is read-only — it reads the SSE stream but cannot ingest or clear data. Session data stays in memory for the duration of the server; nothing is sent to sentry.io unless a DSN is configured.
+
+## Endpoints
+
+| Method | Path | Description |
+|--------|---------------------------------|----------------------------------------------------|
+| `POST` | `/stream` | Envelope ingest |
+| `POST` | `/api/{projectId}/envelope/` | Sentry SDK ingest path |
+| `GET` | `/stream` | Server-Sent Events feed of incoming envelopes |
+| `GET` | `/health` | Liveness check (returns `OK`) |
+
+## Tail output
+
+By default, incoming envelopes are pretty-printed to the terminal:
+
+```
+14:32:01 [ERROR] [SERVER] TypeError: x is not a function [app.ts:42:5] [handleRequest]
+14:32:02 [TRACE] [BROWSER] [http.client] GET /api/users [245ms] [3 spans]
+14:32:03 [INFO] [SERVER] User logged in [user_id=1234] [region=us]
+```
+
+Errors show the exception type, message, and top stack frame. Transactions show the operation, duration, and span count. Logs show the severity level, message, and custom attributes.
+
+Use `--filter` / `-f` to narrow the output to specific event types (repeatable):
+
+```bash
+sentry local -f error -f log # only errors and logs
+```
+
+Use `--quiet` to suppress tail output entirely if you only need the SSE stream.
+
+## Agent tracing
+
+`sentry local` shows rich output for AI agent spans when your SDK instruments with [OpenTelemetry semantic attributes](https://opentelemetry.io/docs/specs/semconv/gen-ai/):
+
+```
+14:32:01 [TRACE] [SERVER] [gen_ai] chat anthropic/claude-4-sonnet [1200ms] [5 spans]
+14:32:02 [TRACE] [SERVER] [mcp] tools/call search_files [320ms]
+14:32:03 [TRACE] [SERVER] [db] SELECT users [postgresql] [12ms]
+14:32:04 [ERROR] [SERVER] RateLimitError: API quota exceeded [api_client.py:42]
+```
+
+GenAI operations show the model name, MCP tool calls show the tool being invoked, and database queries show the system and query summary. This works automatically when your Sentry SDK is configured with AI/LLM integrations.
+
+To watch only agent activity, filter to the `ai` item type:
+
+```bash
+sentry local -f ai # only AI/agent spans
+sentry local -f ai -f error # agent spans and errors
+```
+
+## JSON output
+
+Use `--format json` (or `-F json`) for machine-readable NDJSON output, one JSON object per envelope item:
+
+```bash
+sentry local --format json
+```
+
+`local run` supports the same JSON, attribute, and filter options while it
+starts your app and injects the receiver URL. For an agent-friendly stream
+without SDK housekeeping envelopes, use:
+
+```bash
+sentry local run --format json \
+ --filter error --filter transaction --filter log --filter ai \
+ -- npm run dev
+```
+
+```json
+{"type":"transaction","timestamp":1700000001,"op":"gen_ai","label":"chat anthropic/claude-4-sonnet","duration_ms":1200,"span_count":5,"source":"server"}
+{"type":"error","timestamp":1700000002,"error_type":"RateLimitError","message":"API quota exceeded","source":"server"}
+{"type":"log","timestamp":1700000003,"level":"info","message":"User logged in","attributes":{"user_id":1234},"source":"server"}
+```
+
+This is useful for AI coding agents and automation tools that need to consume Sentry events programmatically.
+
+In JSON mode, event records are versioned NDJSON on standard output. Startup,
+connection, and shutdown messages stay on standard error, so an agent can pipe
+the evidence stream without parsing terminal status text. Records include
+`schema_version`, `trace_id`, and, when supplied by the SDK, `event_id` and
+`envelope_id` for exact correlation. In `local run --format json`, the wrapped
+app's standard output is also forwarded to standard error, leaving standard
+output exclusively for NDJSON observations.
+
+## Agent-debugging fixture
+
+The repository includes a small Hono server that produces a normal database
+request, an agent/MCP trace, and an intentional failure. It sends only to the
+local server unless you explicitly set `SENTRY_DSN`.
+
+In one terminal, start the local receiver:
+
+```bash
+sentry local serve --format json --attributes
+```
+
+In another, run the fixture with Spotlight pointed at that receiver:
+
+```bash
+SENTRY_SPOTLIGHT=http://localhost:8969/stream \
+ pnpm --filter sentry exec tsx test/fixtures/local-agent-server.ts
+```
+
+Then exercise each telemetry shape:
+
+```bash
+curl http://127.0.0.1:3030/api/users/42
+curl -X POST http://127.0.0.1:3030/api/agent/run \
+ -H 'content-type: application/json' \
+ -d '{"prompt":"Where is the rate limit configured?"}'
+curl -i http://127.0.0.1:3030/api/broken
+```
+
+The final request intentionally returns HTTP 500. The fixture is for local
+experimentation only; do not run it with production credentials.
diff --git a/apps/cli-docs/src/fragments/commands/log.md b/apps/cli-docs/src/fragments/commands/log.md
new file mode 100644
index 000000000..0159faa3f
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/log.md
@@ -0,0 +1,105 @@
+
+## Examples
+
+### List logs
+
+```bash
+# List last 100 logs (default)
+sentry log list
+```
+
+```
+TIMESTAMP LEVEL MESSAGE
+2024-01-20 14:22:01 info User login successful
+2024-01-20 14:22:03 debug Processing request for /api/users
+2024-01-20 14:22:05 error Database connection timeout
+2024-01-20 14:22:06 warn Retry attempt 1 of 3
+
+Showing 4 logs.
+```
+
+**Filter logs:**
+
+```bash
+# Show only error logs
+sentry log list -q 'severity:error'
+
+# Filter by message content
+sentry log list -q 'database'
+
+# Limit results
+sentry log list --limit 50
+```
+
+### Stream logs in real-time
+
+```bash
+# Stream with default 2-second poll interval
+sentry log list -f
+
+# Stream with custom 5-second poll interval
+sentry log list -f 5
+
+# Stream error logs from a specific project
+sentry log list my-org/backend -f -q 'severity:error'
+```
+
+### View a log entry
+
+```bash
+sentry log view 968c763c740cfda8b6728f27fb9e9b01
+```
+
+```
+Log 968c763c740c...
+════════════════════
+
+ID: 968c763c740cfda8b6728f27fb9e9b01
+Timestamp: 2024-01-20 14:22:05
+Severity: ERROR
+
+Message:
+ Database connection timeout after 30s
+
+─── Context ───
+
+Project: backend
+Environment: production
+Release: 1.2.3
+
+─── Trace ───
+
+Trace ID: abc123def456abc123def456abc12345
+Span ID: 1234567890abcdef
+
+─── Source Location ───
+
+Function: connect_to_database
+File: src/db/connection.py:142
+```
+
+```bash
+# With explicit project
+sentry log view my-org/backend 968c763c740cfda8b6728f27fb9e9b01
+
+# Open in browser
+sentry log view 968c763c740cfda8b6728f27fb9e9b01 -w
+```
+
+## Finding Log IDs
+
+Log IDs can be found:
+
+1. In the output of `sentry log list` (the ID column)
+2. In the Sentry UI when viewing log entries
+3. In the `sentry.item_id` field of JSON output
+
+## JSON Output
+
+Use `--json` for machine-readable output:
+
+```bash
+sentry log list --json | jq '.data[] | select(.severity == "error")'
+```
+
+In streaming mode with `--json`, each log entry is output as a separate JSON object (newline-delimited JSON), making it suitable for piping to other tools.
diff --git a/apps/cli-docs/src/fragments/commands/monitor.md b/apps/cli-docs/src/fragments/commands/monitor.md
new file mode 100644
index 000000000..fd2f16782
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/monitor.md
@@ -0,0 +1,33 @@
+
+
+## Examples
+
+```bash
+# Wrap a command with cron monitor check-ins (DSN-based)
+SENTRY_DSN=https://examplePublicKey@o0.ingest.sentry.io/0 \
+ sentry monitor run nightly-job -- python manage.py cron
+
+# The -- separator is optional when the command has no flags
+sentry monitor run nightly-job npm run task
+
+# Create/update the monitor on the first check-in via --schedule (crontab)
+sentry monitor run nightly-job -s "0 0 * * *" --max-runtime 30 --timezone UTC -- ./backup.sh
+
+# List cron monitors in an org
+sentry monitor list my-org/
+
+# Paginate through monitors
+sentry monitor list my-org/ -c next
+
+# Output as JSON
+sentry monitor list --json
+```
+
+## Check-in lifecycle
+
+`monitor run` sends an `in_progress` check-in when the wrapped command starts,
+then an `ok` or `error` check-in (with duration) when it finishes, based on the
+exit code. The wrapped command inherits stdio, has `SIGINT`/`SIGTERM`
+forwarded, receives the `SENTRY_MONITOR_SLUG` environment variable, and its
+exit code is preserved. Check-in delivery failures are non-fatal — the wrapped
+command still runs and exits with its own code.
diff --git a/apps/cli-docs/src/fragments/commands/org.md b/apps/cli-docs/src/fragments/commands/org.md
new file mode 100644
index 000000000..d102dfde4
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/org.md
@@ -0,0 +1,36 @@
+
+
+## Examples
+
+```bash
+# List organizations
+sentry org list
+```
+
+```
+SLUG NAME ROLE
+my-org My Organization owner
+another-org Another Org member
+```
+
+```bash
+# View organization details
+sentry org view my-org
+```
+
+```
+Organization: My Organization
+Slug: my-org
+Role: owner
+Projects: 5
+Teams: 3
+Members: 12
+```
+
+```bash
+# Open in browser
+sentry org view my-org -w
+
+# JSON output
+sentry org list --json
+```
diff --git a/apps/cli-docs/src/fragments/commands/platform.md b/apps/cli-docs/src/fragments/commands/platform.md
new file mode 100644
index 000000000..f08f8543c
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/platform.md
@@ -0,0 +1,17 @@
+
+
+## Examples
+
+```bash
+# List all valid Sentry platform identifiers
+sentry platform list
+
+# Filter by substring
+sentry platform list --search python
+
+# Shortcut for `sentry platform list`
+sentry platforms
+
+# Output as JSON
+sentry platform list --json
+```
diff --git a/apps/cli-docs/src/fragments/commands/proguard.md b/apps/cli-docs/src/fragments/commands/proguard.md
new file mode 100644
index 000000000..5f0bf843c
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/proguard.md
@@ -0,0 +1,37 @@
+
+
+## Examples
+
+### Upload mapping files
+
+```bash
+# Upload a ProGuard/R8 mapping file
+sentry proguard upload ./app/build/outputs/mapping/release/mapping.txt
+
+# Upload multiple mapping files
+sentry proguard upload mapping-release.txt mapping-debug.txt
+
+# Validate without uploading (dry-run)
+sentry proguard upload mapping.txt --no-upload
+
+# Fail if no mapping files are provided (useful in CI)
+sentry proguard upload ./mapping/ --require-one
+```
+
+### Compute UUID
+
+```bash
+# Compute the UUID for a ProGuard/R8 mapping file
+sentry proguard uuid ./app/build/outputs/mapping/release/mapping.txt
+
+# Output as JSON (includes the file path)
+sentry proguard uuid mapping.txt --json
+```
+
+## Important Notes
+
+- The UUID is **deterministically derived from the mapping file contents** —
+ identical files always produce the same UUID. This is the same value
+ Sentry uses to associate a mapping with obfuscated Android stack traces.
+- This matches the UUID computed by the legacy `sentry-cli proguard uuid`
+ command byte-for-byte.
diff --git a/apps/cli-docs/src/fragments/commands/project.md b/apps/cli-docs/src/fragments/commands/project.md
new file mode 100644
index 000000000..7756c6324
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/project.md
@@ -0,0 +1,63 @@
+
+
+## Examples
+
+```bash
+# List all projects in an org
+sentry project list my-org/
+```
+
+```
+ORG SLUG PLATFORM TEAM
+my-org frontend javascript web-team
+my-org backend python api-team
+my-org mobile-ios cocoa mobile-team
+```
+
+```bash
+# Filter by platform
+sentry project list my-org/ --platform javascript
+
+# View project details
+sentry project view my-org/frontend
+```
+
+```
+Project: frontend
+Organization: my-org
+Platform: javascript
+Team: web-team
+DSN: https://abc123@sentry.io/123456
+```
+
+```bash
+# Open project in browser
+sentry project view my-org/frontend -w
+```
+
+### Create a project
+
+```bash
+# Every project is a name:platform pair; project names cannot contain whitespace
+# Create a new project
+sentry project create my-new-app:javascript-nextjs
+
+# Create several projects with their own platforms
+sentry project create web:javascript api:python-django worker:node
+
+# Create under a specific org and team
+sentry project create my-org/my-new-app:python --team backend-team
+
+# Preview without creating
+sentry project create my-new-app:node --dry-run
+```
+
+### Delete a project
+
+```bash
+# Delete a project (will prompt for confirmation)
+sentry project delete my-org/old-project
+
+# Delete without confirmation
+sentry project delete my-org/old-project --yes
+```
diff --git a/apps/cli-docs/src/fragments/commands/react-native.md b/apps/cli-docs/src/fragments/commands/react-native.md
new file mode 100644
index 000000000..2d8e5d035
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/react-native.md
@@ -0,0 +1,50 @@
+## Examples
+
+```bash
+# Upload a bundle + sourcemap by debug ID (called by the Gradle plugin)
+sentry react-native gradle \
+ --bundle index.android.bundle \
+ --sourcemap index.android.bundle.map
+
+# Also associate with a release and distribution(s)
+sentry react-native gradle \
+ --bundle index.android.bundle \
+ --sourcemap index.android.bundle.map \
+ --release com.example.app@1.0.0 \
+ --dist 1000
+
+# Xcode build phase (usually added automatically to your build script)
+sentry react-native xcode
+```
+
+## Xcode build step (`react-native xcode`)
+
+`react-native xcode` runs inside an Xcode "Bundle React Native code and images"
+build phase. It has three modes:
+
+- **release build** — wraps the RN build script (standing in for
+ `NODE_BINARY`/`HERMES_CLI_PATH`) to capture the produced bundle + sourcemap
+ (including the Hermes combined sourcemap), then uploads them.
+- **simulator build with `--allow-fetch`** — downloads the bundle + sourcemap
+ from the running packager, then uploads.
+- **debug build** — just runs the build script.
+
+Release/distribution come from `SENTRY_RELEASE`/`SENTRY_DIST` or the app's
+`Info.plist` (`@+`),
+unless `--no-auto-release` is set. When run outside an Xcode build phase the
+release is discovered via `xcodebuild`; `--allow-xcode-infoplist-preprocessing`
+enables `cc`-based `INFOPLIST_PREPROCESS` handling. Pass extra build-script
+arguments after the flags.
+
+## Important Notes
+
+- `react-native gradle` is normally invoked automatically by the
+ [sentry-android-gradle-plugin](https://docs.sentry.io/platforms/react-native/sourcemaps/);
+ you rarely run it by hand.
+- It injects a debug ID into both the bundle and its sourcemap, then uploads
+ them under the `~/` convention. Without `--release` the files are
+ matched by debug ID; with `--release` they are also uploaded for each
+ `--dist`.
+- `--wait`/`--wait-for` block until the server finishes processing the upload.
+- Indexed/file RAM bundles (a pre-Hermes format that React Native has since
+ deprecated) are not supported — use a plain or Hermes bundle.
diff --git a/apps/cli-docs/src/fragments/commands/release.md b/apps/cli-docs/src/fragments/commands/release.md
new file mode 100644
index 000000000..a09cfe71e
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/release.md
@@ -0,0 +1,67 @@
+
+
+## Examples
+
+```bash
+# List releases (auto-detect org)
+sentry release list
+
+# List releases in a specific org
+sentry release list my-org/
+
+# View release details
+sentry release view 1.0.0
+sentry release view my-org/1.0.0
+
+# Create and finalize a release
+sentry release create 1.0.0 --finalize
+
+# Create a release, then finalize separately
+sentry release create 1.0.0
+sentry release set-commits 1.0.0 --auto
+sentry release finalize 1.0.0
+
+# Set commits from local git history
+sentry release set-commits 1.0.0 --local
+
+# Create a deploy
+sentry release deploy 1.0.0 production
+sentry release deploy 1.0.0 staging "Deploy #42"
+
+# Propose a version from git HEAD
+sentry release create $(sentry release propose-version)
+
+# List deploys for a release
+sentry release deploys 1.0.0
+sentry release deploys my-org/1.0.0
+
+# Archive a release (hide it from the default list, but keep it)
+sentry release archive 1.0.0
+sentry release archive my-org/1.0.0 --dry-run # Preview without archiving
+
+# Restore a previously archived release
+sentry release restore 1.0.0
+sentry release restore my-org/1.0.0
+
+# Delete a release
+sentry release delete my-org/1.0.0
+sentry release delete my-org/1.0.0 --yes # Skip confirmation
+sentry release delete my-org/1.0.0 --dry-run # Preview without deleting
+
+# Output as JSON
+sentry release list --json
+sentry release view 1.0.0 --json
+
+# Full release workflow with explicit org
+sentry release create my-org/1.0.0 --project my-project
+sentry release set-commits my-org/1.0.0 --auto
+sentry release finalize my-org/1.0.0
+sentry release deploy my-org/1.0.0 production
+```
+
+## Important Notes
+
+- **Version matching**: The release version must match the `release` value in your `Sentry.init()` call. If your SDK uses `"1.0.0"`, create the release as `sentry release create org/1.0.0` (version = `1.0.0`), **not** `sentry release create org/myapp/1.0.0`.
+- **The `org/` prefix is the org slug**: In `sentry release create sentry/1.0.0`, `sentry` is the org slug and `1.0.0` is the version. The `/` separates org from version — it is not part of the version string.
+- **`--auto` needs a git checkout**: The `--auto` flag lists repos from the Sentry API and matches against your local `origin` remote URL. Without a local git repo, use `--local` instead.
+- **Default mode tries `--auto` first**: When neither `--auto` nor `--local` is specified, `set-commits` tries auto-discovery first and falls back to local git history if the integration isn't configured.
diff --git a/apps/cli-docs/src/fragments/commands/replay.md b/apps/cli-docs/src/fragments/commands/replay.md
new file mode 100644
index 000000000..71ea5a672
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/replay.md
@@ -0,0 +1,41 @@
+
+## Examples
+
+### List replays
+
+```bash
+# List recent replays for a project
+sentry replay list my-org/frontend
+
+# Search across all projects in an org
+sentry replay list my-org/ --query "environment:production"
+
+# Change the time window and sort
+sentry replay list my-org/frontend --period 24h --sort errors
+
+# Paginate through results
+sentry replay list my-org/frontend -c next
+sentry replay list my-org/frontend -c prev
+
+# Output machine-readable data
+sentry replay list my-org/frontend --json
+```
+
+### View a replay
+
+```bash
+# View a replay by ID using auto-detected org/project context
+sentry replay view 346789a703f6454384f1de473b8b9fcc
+
+# View a replay with an explicit org
+sentry replay view my-org/346789a703f6454384f1de473b8b9fcc
+
+# View a replay with explicit org/project context
+sentry replay view my-org/frontend/346789a703f6454384f1de473b8b9fcc
+
+# Open a replay in the browser
+sentry replay view my-org/346789a703f6454384f1de473b8b9fcc --web
+
+# View the replay linked to a trace
+sentry replay view my-org/frontend/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+```
diff --git a/apps/cli-docs/src/fragments/commands/repo.md b/apps/cli-docs/src/fragments/commands/repo.md
new file mode 100644
index 000000000..e62b2a747
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/repo.md
@@ -0,0 +1,14 @@
+
+
+## Examples
+
+```bash
+# List repositories (auto-detect org)
+sentry repo list
+
+# List repos in a specific org with pagination
+sentry repo list my-org/ -c next
+
+# Output as JSON
+sentry repo list --json
+```
diff --git a/apps/cli-docs/src/fragments/commands/schema.md b/apps/cli-docs/src/fragments/commands/schema.md
new file mode 100644
index 000000000..79ae96ee9
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/schema.md
@@ -0,0 +1,26 @@
+
+
+## Examples
+
+```bash
+# List all API resources
+sentry schema
+
+# Browse issue endpoints
+sentry schema issues
+
+# View details for a specific operation
+sentry schema issues list
+
+# Look up an endpoint by its exact operation ID
+sentry schema listOrganizationEvents
+
+# Look up an endpoint by HTTP method and path
+sentry schema "GET /api/0/organizations/{organization_id_or_slug}/issues/"
+
+# Search for monitoring-related endpoints
+sentry schema --search monitor
+
+# Flat list of every endpoint
+sentry schema --all
+```
diff --git a/apps/cli-docs/src/fragments/commands/snapshots.md b/apps/cli-docs/src/fragments/commands/snapshots.md
new file mode 100644
index 000000000..ee58e6f1f
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/snapshots.md
@@ -0,0 +1,56 @@
+
+
+## Examples
+
+```bash
+# Upload a folder of screenshots as a snapshot for an app
+sentry snapshots upload ./screenshots --app-id com.example.app
+
+# Upload only a subset of images (removals/renames not inferred on PRs)
+sentry snapshots upload ./screenshots --app-id my-app --selective
+
+# Only flag images that differ by more than 1%
+sentry snapshots upload ./screenshots --app-id my-app --diff-threshold 0.01
+
+# Compare two directories of snapshot images locally
+sentry snapshots diff ./baseline ./head
+
+# Fail (non-zero exit) if any images changed, with a custom threshold
+sentry snapshots diff ./baseline ./head --fail-on-diff --threshold 0.02
+
+# Download a specific baseline snapshot by ID
+sentry snapshots download --snapshot-id 1234567890
+
+# Download the latest baseline for an app, filtered by branch
+sentry snapshots download --app-id my-app --branch main
+
+# Extract images to a specific directory
+sentry snapshots download --app-id my-app --output ./baseline/
+```
+
+## Important Notes
+
+- `snapshots upload` scans a folder for PNG/JPEG images (skipping hidden files),
+ uploads each to Sentry's object store — images already present are skipped by
+ content hash — and creates a snapshot. **Sentry SaaS only.** A companion
+ `.json` sidecar adds per-image metadata; `--all-image-file-names`
+ (or `--all-image-file-names-file`) lists the full suite for selective uploads.
+ Each image must be at most 40,000,000 pixels. Git metadata is auto-collected
+ in CI (see `build upload`); a `--pr-number` requires a resolvable base SHA.
+- `snapshots diff` compares two local image directories (PNG/JPEG) perceptually
+ — anti-aliasing aware, with a per-pixel `--threshold` (0.0–1.0) — and writes a
+ PNG diff mask per changed image. It makes **no network requests**. Use
+ `--fail-on-diff` to exit non-zero when any images changed/added/removed, and
+ `--selective` to treat images missing from head as skipped rather than removed.
+- `snapshots download` fetches baseline snapshot images from Sentry's preprod
+ system and extracts them to a local directory. **Sentry SaaS only.**
+- Provide exactly one of `--snapshot-id` (a direct artifact ID) or `--app-id`
+ (resolves the latest baseline). `--branch` only applies with `--app-id`.
+- With org auth tokens, resolving by `--app-id` requires `--project` (a project
+ ID or slug).
+- If the downloadable archive has not been built yet, the command triggers a
+ build and waits for it (up to 5 minutes).
+- Images are extracted to `./snapshots-base/` by default; override with
+ `--output`.
+- The organization is resolved from `--org`, `SENTRY_ORG`, config defaults, or a
+ detected DSN.
diff --git a/apps/cli-docs/src/fragments/commands/sourcemap.md b/apps/cli-docs/src/fragments/commands/sourcemap.md
new file mode 100644
index 000000000..77c8aa307
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/sourcemap.md
@@ -0,0 +1,79 @@
+
+
+## Examples
+
+### Inject debug IDs
+
+`sentry sourcemap inject` is a purely local file operation — it does not make any API calls and does not require authentication. You can run it in CI before authenticating.
+
+Existing debug IDs from JavaScript comments or sourcemaps are preserved. If a
+bundler emits an ID without Sentry's runtime registration snippet, `inject`
+adds the snippet and adjusts the sourcemap mappings. Bundles that already
+register their ID are left unchanged. `sourcemap upload` applies the same
+injection by default, unless `--no-rewrite` is passed.
+Binary bundles with an existing debug ID in their sourcemap are left unchanged.
+
+```bash
+# Inject debug IDs into all JS files in dist/
+sentry sourcemap inject ./dist
+
+# Preview changes without writing
+sentry sourcemap inject ./dist --dry-run
+
+# Only process specific extensions
+sentry sourcemap inject ./build --ext .js,.mjs
+```
+
+### Upload sourcemaps
+
+```bash
+# Upload sourcemaps from dist/
+sentry sourcemap upload ./dist
+
+# Associate with a release
+sentry sourcemap upload ./dist --release 1.0.0
+
+# Set a custom URL prefix
+sentry sourcemap upload ./dist --url-prefix '~/static/js/'
+```
+
+### Resolve sourcemap linkage
+
+```bash
+# Report how each JS file's sourcemap resolves and whether a debug ID
+# has been injected (read-only — never modifies files)
+sentry sourcemap resolve ./dist
+
+# Machine-readable output
+sentry sourcemap resolve ./dist --json
+```
+
+Use `sentry sourcemap resolve` to debug why `sentry sourcemap upload` may
+not find the expected sourcemaps. It reports, for each JavaScript file,
+whether the companion `.map` was located (by convention or via a
+`sourceMappingURL` directive), whether the map is inline (`data:` URL) or
+remote, and whether a Sentry debug ID is present.
+
+## Error handling
+
+Both `sentry sourcemap inject` and `sentry sourcemap upload` exit with an
+error if zero JS + sourcemap pairs are discovered in the target
+directory. This catches silent bundler misconfigurations — the most
+common cause is a bundler that isn't emitting `.map` files:
+
+```
+# Vite / Astro: set `vite.build.sourcemap: "hidden"` (Astro 5) or
+# `vite.environments.client.build.sourcemap: "hidden"` (Astro 6+).
+
+# webpack: set `devtool: "hidden-source-map"`.
+
+# esbuild: set `sourcemap: true` or `sourcemap: "linked"`.
+```
+
+For CI steps that may run against legitimately-empty directories (e.g.,
+library-only repos, conditional release skips), pass `--allow-empty` to
+suppress the error:
+
+```bash
+sentry sourcemap upload ./dist --allow-empty
+```
diff --git a/apps/cli-docs/src/fragments/commands/span.md b/apps/cli-docs/src/fragments/commands/span.md
new file mode 100644
index 000000000..2cece9e9f
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/span.md
@@ -0,0 +1,48 @@
+
+
+## Examples
+
+### List spans
+
+```bash
+# List recent spans in the current project
+sentry span list
+
+# Find all DB spans
+sentry span list -q "op:db"
+
+# Slow spans in the last 24 hours
+sentry span list -q "duration:>100ms" --period 24h
+
+# List spans within a specific trace
+sentry span list abc123def456abc123def456abc12345
+
+# Paginate through results
+sentry span list -c next
+```
+
+### Filter by project in a trace
+
+```bash
+# Show only spans from one project within a trace
+sentry span list my-org/cli-server/abc123def456abc123def456abc12345
+
+# Or use --query to filter by project
+sentry span list abc123def456abc123def456abc12345 -q "project:cli-server"
+
+# Multiple projects at once
+sentry span list abc123def456abc123def456abc12345 -q "project:[cli-server,api]"
+```
+
+### View spans
+
+```bash
+# View a single span
+sentry span view abc123def456abc123def456abc12345 a1b2c3d4e5f67890
+
+# View multiple spans at once
+sentry span view abc123def456abc123def456abc12345 a1b2c3d4e5f67890 b2c3d4e5f6789012
+
+# With explicit org/project
+sentry span view my-org/backend/abc123def456abc123def456abc12345 a1b2c3d4e5f67890
+```
diff --git a/apps/cli-docs/src/fragments/commands/status.md b/apps/cli-docs/src/fragments/commands/status.md
new file mode 100644
index 000000000..40b5e6639
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/status.md
@@ -0,0 +1,29 @@
+
+
+## Examples
+
+```bash
+# Show the current status of Sentry's services
+sentry status
+```
+
+```
+✓ All Systems Operational
+
+### Components
+
+● Dashboard — Operational
+● US Error Ingestion — Operational
+
+See https://status.sentry.io for full details.
+```
+
+```bash
+# Get machine-readable status (useful in scripts)
+sentry status --json
+```
+
+```bash
+# Check a self-hosted or regional status page (Statuspage CNAME)
+sentry status --url https://status.acme.com
+```
diff --git a/apps/cli-docs/src/fragments/commands/team.md b/apps/cli-docs/src/fragments/commands/team.md
new file mode 100644
index 000000000..3606123f4
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/team.md
@@ -0,0 +1,14 @@
+
+
+## Examples
+
+```bash
+# List teams
+sentry team list my-org/
+
+# Paginate through teams
+sentry team list my-org/ -c next
+
+# Output as JSON
+sentry team list --json
+```
diff --git a/apps/cli-docs/src/fragments/commands/trace.md b/apps/cli-docs/src/fragments/commands/trace.md
new file mode 100644
index 000000000..cd2924ea2
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/trace.md
@@ -0,0 +1,61 @@
+
+
+## Examples
+
+### List traces
+
+```bash
+# List last 20 traces (default)
+sentry trace list
+
+# Sort by slowest first
+sentry trace list --sort duration
+
+# Filter by transaction name, last 24 hours
+sentry trace list -q "transaction:GET /api/users" --period 24h
+
+# Paginate through results
+sentry trace list my-org/backend -c next
+```
+
+### View a trace
+
+```bash
+# View trace details with span tree
+sentry trace view abc123def456abc123def456abc12345
+
+# Open trace in browser
+sentry trace view abc123def456abc123def456abc12345 -w
+
+# Auto-recover from an issue short ID
+sentry trace view PROJ-123
+```
+
+### Cross-project traces
+
+```bash
+# Filter trace view to one project's spans
+sentry trace view my-org/cli-server/abc123def456abc123def456abc12345
+
+# Full trace across all projects (default)
+sentry trace view my-org/abc123def456abc123def456abc12345
+
+# Filter trace logs by project
+sentry trace logs my-org/cli-server/abc123def456abc123def456abc12345
+
+# Multiple projects via --query
+sentry trace logs abc123def456abc123def456abc12345 -q "project:[cli-server,api]"
+```
+
+### View trace logs
+
+```bash
+# View logs for a trace
+sentry trace logs abc123def456abc123def456abc12345
+
+# Search with a longer time window
+sentry trace logs --period 30d abc123def456abc123def456abc12345
+
+# Filter logs within a trace
+sentry trace logs -q 'severity:error' abc123def456abc123def456abc12345
+```
diff --git a/apps/cli-docs/src/fragments/commands/trial.md b/apps/cli-docs/src/fragments/commands/trial.md
new file mode 100644
index 000000000..0847a9be6
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/trial.md
@@ -0,0 +1,20 @@
+
+
+## Examples
+
+```bash
+# List all trials for the current org
+sentry trial list
+
+# List trials for a specific org
+sentry trial list my-org
+
+# Start a Seer trial
+sentry trial start seer
+
+# Start a trial for a specific org
+sentry trial start replays my-org
+
+# Start a Business plan trial (opens browser)
+sentry trial start plan
+```
diff --git a/apps/cli-docs/src/fragments/commands/wasm-split.md b/apps/cli-docs/src/fragments/commands/wasm-split.md
new file mode 100644
index 000000000..3b768a444
--- /dev/null
+++ b/apps/cli-docs/src/fragments/commands/wasm-split.md
@@ -0,0 +1,41 @@
+
+## Examples
+
+```bash
+# Add a build id to a module, in place, and print it
+sentry wasm-split app.wasm
+
+# Capture the build id for a later upload
+BUILD_ID=$(sentry wasm-split app.wasm)
+
+# Split debug data into a companion and ship a stripped binary
+sentry wasm-split app.wasm --debug-out app.debug.wasm --strip
+
+# Also drop function names from the shipped binary
+sentry wasm-split app.wasm -d app.debug.wasm --strip --strip-names
+
+# Point browsers at a companion served from a CDN
+sentry wasm-split app.wasm -o dist/app.wasm -d dist/app.debug.wasm --strip \
+ --external-dwarf-url https://cdn.example.com/debug/app.debug.wasm
+```
+
+## Important Notes
+
+- This is a **drop-in replacement for Symbolicator's `wasm-split` binary** —
+ same flags, same behaviour, same output.
+- **Only the build id is printed**, as lowercase hex, so it can be captured in
+ a shell variable. Pass `--quiet` to print nothing. `--quiet` cannot be
+ combined with `--json`.
+- A build id the module **already carries is reused**. Rewriting it would
+ orphan debug files uploaded against the old one. `--build-id` applies only
+ when the module has none.
+- The debug companion is a **complete copy** of the module, captured before
+ stripping. DWARF offsets are relative to the code section, so a companion
+ missing it cannot be symbolicated. Expect it to be about the size of the
+ input.
+- `--strip-names` takes effect **only alongside `--strip`**.
+- `external_debug_info` is written when `--external-dwarf-url` is given, or
+ else from the basename of `--debug-out`. A bare filename resolves relative to
+ the main wasm file, which is how Emscripten reads it.
+- The file is **left untouched when nothing changed** — no new build id, no
+ stripping, no `external_debug_info` — so reruns do not disturb build caches.
diff --git a/apps/cli-docs/src/fragments/configuration.md b/apps/cli-docs/src/fragments/configuration.md
new file mode 100644
index 000000000..b9673a933
--- /dev/null
+++ b/apps/cli-docs/src/fragments/configuration.md
@@ -0,0 +1,126 @@
+
+
+## Configuration File (`.sentryclirc`)
+
+The CLI supports a `.sentryclirc` config file using standard INI syntax. This is the same format used by the legacy `sentry-cli` tool, so existing config files are automatically picked up.
+
+### How It Works
+
+The CLI looks for `.sentryclirc` files by walking up from your current directory toward the filesystem root. If multiple files are found, values from the closest file take priority, with `~/.sentryclirc` serving as a global fallback.
+
+```ini
+[defaults]
+org = my-org
+project = my-project
+
+[auth]
+token = sntrys_...
+```
+
+### Supported Fields
+
+| Section | Key | Description |
+|---------|-----|-------------|
+| `[defaults]` | `org` | Default organization slug |
+| `[defaults]` | `project` | Default project slug |
+| `[defaults]` | `url` | Sentry base URL (for self-hosted) |
+| `[auth]` | `token` | Auth token (mapped to `SENTRY_AUTH_TOKEN`) |
+
+### Monorepo Setup
+
+In monorepos, place a `.sentryclirc` at the repo root with your org, then add per-package configs with just the project:
+
+```
+my-monorepo/
+ .sentryclirc # [defaults] org = my-company
+ packages/
+ frontend/
+ .sentryclirc # [defaults] project = frontend-web
+ backend/
+ .sentryclirc # [defaults] project = backend-api
+```
+
+When you run a command from `packages/frontend/`, the CLI resolves `org = my-company` from the root and `project = frontend-web` from the closest file.
+
+### Resolution Priority
+
+When the CLI needs to determine your org and project, it checks these sources in order:
+
+1. **Explicit CLI arguments** — `sentry issue list my-org/my-project`
+2. **Environment variables** — `SENTRY_ORG` / `SENTRY_PROJECT`
+3. **`.sentryclirc` config file** — walked up from CWD, merged with `~/.sentryclirc`
+4. **Persistent defaults** — set via `sentry cli defaults`
+5. **DSN auto-detection** — scans source code and `.env` files
+6. **Directory name inference** — matches your directory name against project slugs
+
+The first source that provides both org and project wins. For org-only commands, only the org is needed.
+
+### Backward Compatibility
+
+If you previously used the legacy `sentry-cli` and have a `~/.sentryclirc` file, the new CLI reads it automatically. The `[defaults]` and `[auth]` sections are fully compatible. The `[auth] token` value is mapped to the `SENTRY_AUTH_TOKEN` environment variable internally (only if the env var is not already set).
+
+## Persistent Defaults
+
+Use `sentry cli defaults` to set persistent defaults for organization, project, URL, and telemetry. These are stored in the CLI's local database and apply to all commands.
+
+```bash
+sentry cli defaults org my-org # Set default organization
+sentry cli defaults project my-project # Set default project
+sentry cli defaults url https://... # Set Sentry URL (self-hosted)
+sentry cli defaults telemetry off # Disable telemetry
+sentry cli defaults # Show all current defaults
+sentry cli defaults org --clear # Clear a specific default
+```
+
+See [`sentry cli defaults`](./commands/cli/#sentry-cli-defaults) for full usage.
+
+## Global Options
+
+These flags are accepted by every command. They are not shown in individual command `--help` output, but are always available.
+
+### `--log-level `
+
+Set the log verbosity level. Accepts: `error`, `warn`, `log`, `info`, `debug`, `trace`.
+
+```bash
+sentry issue list --log-level debug
+sentry --log-level=trace cli upgrade
+```
+
+Overrides `SENTRY_LOG_LEVEL` when both are set.
+
+### `--verbose`
+
+Shorthand for `--log-level debug`. Enables debug-level diagnostic output.
+
+```bash
+sentry issue list --verbose
+```
+
+:::note
+The `sentry api` command also uses `--verbose` to show full HTTP request/response details. When used with `sentry api`, it serves both purposes (debug logging + HTTP output).
+:::
+
+## Credential Storage
+
+We store credentials and caches in a SQLite database (`cli.db`) inside the config directory. The location follows the [XDG Base Directory specification](https://specifications.freedesktop.org/basedir/latest/): by default the CLI uses `$XDG_CONFIG_HOME/sentry` (i.e. `~/.config/sentry/` when `XDG_CONFIG_HOME` is unset), and you can override it with `SENTRY_CONFIG_DIR`. For backward compatibility, if a legacy `~/.sentry/` directory already exists it continues to be used. The database file and its WAL side-files are created with restricted permissions (mode 600) so that only the current user can read them. The database also caches:
+
+- Organization and project defaults
+- DSN resolution results
+- Region URL mappings
+- Project aliases (for monorepo support)
+
+See [Credential Storage](./commands/auth/#credential-storage) in the auth command docs for more details.
+
+## Binary Install Location
+
+When installed via the install script, the CLI binary is placed in an XDG-aligned directory. `sentry cli setup` resolves the location in this order:
+
+1. `SENTRY_INSTALL_DIR` — explicit override
+2. `$XDG_BIN_HOME` — used when set to an absolute path, per the XDG spec
+3. `~/.local/bin` or `~/bin` — when either already exists and is on your `PATH`
+4. `~/.local/bin` — default fallback
+
+Older installs placed the binary in `~/.sentry/bin`. Running `sentry cli setup` moves an existing `~/.sentry/bin` binary into the resolved install directory (updating your `PATH` and recorded install metadata to match) and migrates any legacy `~/.sentry` config data (`cli.db`, `config.json`) into the XDG config directory. Both migrations are skipped when a binary or config already exists at the target.
+
+`sentry upgrade` runs `setup` on the new binary, so it migrates too — but conservatively, because upgrade never edits your `PATH`. A legacy `~/.sentry/bin` binary is relocated to the XDG install directory **only when that directory is already on your `PATH`**, so the moved binary stays discoverable. If the XDG directory isn't on `PATH`, upgrade leaves the binary in place (a mislocated binary that vanished from `PATH` would break the command); run `sentry cli setup` explicitly to relocate it and update `PATH`. Legacy config data is migrated on upgrade regardless.
diff --git a/apps/cli-docs/src/styles/cli.css b/apps/cli-docs/src/styles/cli.css
new file mode 100644
index 000000000..ea247d8e2
--- /dev/null
+++ b/apps/cli-docs/src/styles/cli.css
@@ -0,0 +1,640 @@
+/* ==========================================================================
+ Sentry CLI Docs — Project-specific overrides
+ Base theme provided by @sentry/starlight-theme
+ ========================================================================== */
+
+/* Brand fonts: Dammit Sans (headings, self-hosted below) + Rubik (body, self-
+ hosted by @sentry/starlight-theme as "Rubik Variable") + JetBrains Mono
+ (code). Dammit Sans is a display face with a limited glyph set, so headings
+ fall back to Rubik for any missing glyph (e.g. # / * \ | ^ _ ` ~). */
+@font-face {
+ font-family: "Dammit Sans";
+ /* Relative URL so Vite bundles + hashes it and respects the site `base`
+ (root-absolute paths break under the PR-preview base, e.g. /_preview/pr-N). */
+ src: url("../fonts/dammit-sans-v0.3-bold.otf") format("opentype");
+ font-weight: 700;
+ font-style: normal;
+ font-display: swap;
+}
+
+/* Headings use the Dammit Sans display font; body/code are unaffected. */
+.sl-markdown-content :is(h1, h2, h3, h4, h5, h6):not(:where(.not-content *)),
+.page-title-wrapper h1,
+.hero h1 {
+ font-family: var(--sl-font-headings);
+}
+
+:root,
+:root[data-theme="dark"],
+:root[data-theme="light"] {
+ /* Compat: existing components reference this var */
+ /* Sentry brand purple (#7553FF), the mid-anchor of the CLI banner gradient */
+ --sl-color-accent-rgb: 117 83 255;
+
+ /* Body uses the theme's self-hosted Rubik Variable; headings use Dammit Sans;
+ code stays JetBrains Mono. */
+ --sl-font: "Rubik Variable", -apple-system, BlinkMacSystemFont, "Segoe UI",
+ sans-serif;
+ --sl-font-headings: "Dammit Sans", "Rubik Variable", -apple-system,
+ BlinkMacSystemFont, "Segoe UI", sans-serif;
+ --sl-font-mono: "JetBrains Mono", ui-monospace, monospace;
+
+ /* Match the original CLI docs header sizing. */
+ --sl-nav-height: 4rem !important;
+ --sl-nav-pad-x: 1.5rem !important;
+ --sl-nav-gap: 2rem !important;
+}
+
+/* Subtle background gradient glow */
+.main-frame::before {
+ content: "";
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 100vh;
+ background:
+ radial-gradient(
+ ellipse 80% 50% at 50% -20%,
+ rgb(var(--sl-color-accent-rgb) / 0.12) 0%,
+ transparent 50%
+ ),
+ radial-gradient(
+ ellipse 60% 40% at 100% 0%,
+ rgba(59, 130, 246, 0.08) 0%,
+ transparent 40%
+ );
+ pointer-events: none;
+ z-index: -1;
+}
+
+/* GitHub icon - white in header */
+.social-icons a,
+.social-icons a svg,
+a[href*="github.com"],
+a[href*="github.com"] svg {
+ color: #fff !important;
+ fill: #fff !important;
+}
+
+.social-icons a:hover,
+.social-icons a:hover svg,
+a[href*="github.com"]:hover,
+a[href*="github.com"]:hover svg {
+ color: rgba(255, 255, 255, 0.8) !important;
+ fill: rgba(255, 255, 255, 0.8) !important;
+}
+
+/* Header / Navigation */
+header.header {
+ background: rgba(10, 10, 15, 0.95);
+ backdrop-filter: blur(12px);
+ border-bottom: none;
+}
+
+header.header > .header {
+ display: flex;
+ gap: 1rem;
+ justify-content: space-between;
+ align-items: center;
+ height: 100%;
+ width: 100%;
+ padding: 0;
+ background: transparent;
+ backdrop-filter: none;
+}
+
+.header-left {
+ flex: 0 0 auto;
+}
+
+.header-right {
+ flex: 0 0 auto;
+ gap: 1rem;
+ align-items: center;
+}
+
+.site-title img {
+ height: 2.3rem !important;
+ width: auto !important;
+}
+
+starlight-menu-button button {
+ background-color: #1a1a1f !important;
+ color: #fff !important;
+ border: 1px solid rgba(255, 255, 255, 0.2) !important;
+ box-shadow: none !important;
+}
+
+site-search {
+ --sl-search-width: 22rem;
+}
+
+site-search button {
+ background: var(--sl-color-bg) !important;
+ border: 1px solid rgba(255, 255, 255, 0.25) !important;
+ border-radius: 6px !important;
+ padding: 0.35rem 0.75rem !important;
+ font-size: 0.8rem !important;
+ height: auto !important;
+ min-height: unset !important;
+ gap: 0.5rem !important;
+ width: 22rem !important;
+ min-width: 22rem !important;
+ justify-content: space-between !important;
+ position: fixed !important;
+ left: 50% !important;
+ transform: translateX(-50%) !important;
+ top: 0.75rem !important;
+ z-index: 1000 !important;
+}
+
+site-search button:hover {
+ border-color: rgba(255, 255, 255, 0.4) !important;
+}
+
+site-search button svg {
+ width: 14px !important;
+ height: 14px !important;
+ color: rgba(255, 255, 255, 0.5) !important;
+}
+
+site-search button span {
+ font-size: 0.8rem !important;
+ color: rgba(255, 255, 255, 0.5) !important;
+}
+
+site-search button kbd {
+ background: transparent !important;
+ border: none !important;
+ color: rgba(255, 255, 255, 0.5) !important;
+ font-size: 0.75rem !important;
+ padding: 0 !important;
+ box-shadow: none !important;
+}
+
+/* Docs section heading accent - keep it below the text, not through it. */
+html[data-has-sidebar] .sl-markdown-content h2:not(:where(.not-content *))::before {
+ bottom: 0.02em;
+ height: 0.12em;
+ left: 0;
+ right: var(--sl-anchor-icon-space, 0);
+ transform: none;
+}
+
+/* ==========================================================================
+ Splash / Landing Page Styles
+ ========================================================================== */
+
+/* Hero section */
+.hero {
+ padding: 10rem 0 0.5rem !important;
+ text-align: left !important;
+ max-width: none !important;
+}
+
+.hero > img,
+.hero > .hero-image {
+ display: none !important;
+}
+
+/* Hero title - make it bold and impactful */
+.hero h1 {
+ font-size: clamp(2.5rem, 8vw, 4rem) !important;
+ font-weight: 700 !important;
+ line-height: 1 !important;
+ letter-spacing: -0.03em !important;
+ margin-bottom: 1.5rem !important;
+}
+
+/* Hero tagline */
+.hero .tagline {
+ font-size: 1.125rem !important;
+ line-height: 1.7 !important;
+ color: rgba(255, 255, 255, 0.6) !important;
+ max-width: 540px !important;
+ margin: 0 !important;
+}
+
+/* Hide default hero actions */
+.hero .action,
+.hero .sl-flex.actions {
+ display: none !important;
+}
+
+/* Command box in hero - tight to tagline, space below */
+.hero-command {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.75rem;
+ margin-top: 0.5rem;
+ margin-bottom: 4rem;
+}
+
+.hero-docs-link {
+ color: rgba(255, 255, 255, 0.7) !important;
+ text-decoration: none !important;
+ font-weight: 400;
+ font-size: 0.9rem;
+ transition: color 0.2s ease;
+ margin-top: 0.75rem;
+ position: relative;
+ display: inline-block;
+}
+
+.hero-docs-link::after {
+ content: "";
+ position: absolute;
+ bottom: -3px;
+ right: 0;
+ width: 7.5em; /* Width of "documentation." */
+ height: 1px;
+ background: currentColor;
+ transition: width 0.3s ease;
+}
+
+.hero-docs-link:hover {
+ color: #fff !important;
+}
+
+.hero-docs-link:hover::after {
+ width: 100%;
+}
+
+/* Remove the static underline from the span since pseudo-element handles it */
+.hero-docs-link .underline {
+ text-decoration: none;
+}
+
+/* Stack container - page layout for splash.
+ Keep the landing page at the pre-theme width; normal docs pages use the
+ shared Sentry Starlight theme defaults. */
+html[data-has-hero]:not([data-has-sidebar]) {
+ --sl-content-width: 67.5rem !important;
+ --sl-content-pad-x: 2rem !important;
+}
+
+@media (max-width: 49.999rem) {
+ html[data-has-hero]:not([data-has-sidebar]) {
+ --sl-nav-height: 3.5rem !important;
+ --sl-nav-pad-x: 1rem !important;
+ }
+}
+
+@media (min-width: 50rem) {
+ html[data-has-hero]:not([data-has-sidebar]) {
+ --sl-nav-height: 4rem !important;
+ --sl-nav-pad-x: 1.5rem !important;
+ }
+}
+
+html[data-has-hero]:not([data-has-sidebar]) header.header {
+ background: rgba(10, 10, 15, 0.95);
+ border-bottom: none;
+}
+
+html[data-has-hero]:not([data-has-sidebar]) header.header > .header {
+ background: transparent;
+ backdrop-filter: none;
+}
+
+html[data-has-hero]:not([data-has-sidebar]) .header-homepage {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1rem;
+ height: 100%;
+ width: 100%;
+ max-width: var(--sl-content-width);
+ margin: 0 auto;
+ padding: 0;
+}
+
+html[data-has-hero]:not([data-has-sidebar]) .header-left {
+ flex: 0 0 auto;
+}
+
+html[data-has-hero]:not([data-has-sidebar]) .header-right {
+ display: flex;
+ flex: 0 0 auto;
+ align-items: center;
+ gap: 1rem;
+ margin-left: auto;
+}
+
+html[data-has-hero]:not([data-has-sidebar]) .content-panel {
+ padding: 1.5rem var(--sl-content-pad-x);
+}
+
+html[data-has-hero]:not([data-has-sidebar]) .sl-container {
+ max-width: var(--sl-content-width);
+ margin: 0 auto;
+ padding: 0;
+}
+
+html[data-has-hero]:not([data-has-sidebar]) main > .content-panel:first-child .sl-container > * + * {
+ margin-top: 1.5rem;
+}
+
+html[data-has-hero]:not([data-has-sidebar]) .sl-markdown-content {
+ line-height: 1.8;
+}
+
+html[data-has-hero]:not([data-has-sidebar]) .command-text {
+ font-size: 0.875em;
+ font-weight: 500;
+ line-height: 1;
+ padding: 0.2em 0.5em;
+}
+
+html[data-has-hero]:not([data-has-sidebar]) main {
+ padding-top: 0;
+}
+
+/* ==========================================================================
+ Features Section - Horizontal Rows with Terminals
+ ========================================================================== */
+
+.feature-section {
+ margin: 2rem 0;
+ background: rgba(255, 255, 255, 0.02);
+ border-radius: 16px;
+ overflow: hidden;
+}
+
+.feature-section-inner {
+ display: flex;
+ align-items: stretch;
+}
+
+.feature-section-inner.reverse {
+ flex-direction: row-reverse;
+}
+
+.feature-text {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ padding: 2.5rem;
+}
+
+.feature-text h3 {
+ font-size: 1.75rem;
+ font-weight: 600;
+ color: #fff;
+ margin: 0 0 0.75rem 0;
+ line-height: 1.2;
+}
+
+.feature-text p {
+ font-size: 1.05rem;
+ line-height: 1.7;
+ color: rgba(255, 255, 255, 0.55);
+ margin: 0 0 1rem 0;
+}
+
+.feature-text p:last-child {
+ margin-bottom: 0;
+ color: rgba(255, 255, 255, 0.45);
+}
+
+.feature-text code {
+ background: rgba(255, 255, 255, 0.1);
+ padding: 0.15em 0.4em;
+ border-radius: 4px;
+ font-size: 0.9em;
+}
+
+.feature-visual {
+ flex: 0 0 55%;
+ min-width: 0;
+ min-height: 420px;
+ margin: 0.5rem 0.75rem 0.75rem 0;
+ padding: 10%;
+ background-size: cover;
+ background-position: center;
+ border-radius: 8px;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-sizing: border-box;
+ overflow: hidden;
+}
+
+.feature-section-inner.reverse .feature-visual {
+ margin: 0.75rem 0 0.75rem 0.75rem;
+}
+
+/* Responsive for feature sections */
+@media (max-width: 1000px) {
+ .feature-section-inner,
+ .feature-section-inner.reverse {
+ flex-direction: column;
+ }
+
+ .feature-text {
+ padding: 2rem;
+ text-align: center;
+ }
+
+ .feature-visual,
+ .feature-section-inner.reverse .feature-visual {
+ flex: none;
+ width: calc(100% - 1.5rem);
+ min-height: 380px;
+ margin: 0 0.75rem 0.75rem 0.75rem;
+ }
+}
+
+@media (max-width: 600px) {
+ .feature-section {
+ margin: 1.5rem 0;
+ }
+
+ .feature-text {
+ padding: 1.5rem;
+ }
+
+ .feature-visual {
+ min-height: 340px;
+ margin: 0 0.5rem 0.5rem 0.5rem;
+ padding: 8%;
+ }
+
+ .feature-text h3 {
+ font-size: 1.4rem;
+ }
+}
+
+@media (max-width: 480px) {
+ .feature-visual {
+ margin: 0 0.5rem 0.5rem 0.5rem;
+ min-height: 300px;
+ padding: 6%;
+ }
+
+ .feature-text {
+ padding: 1.25rem;
+ }
+
+ .feature-text h3 {
+ font-size: 1.25rem;
+ }
+}
+
+/* ==========================================================================
+ PackageManagerCode - pm-pre no-border treatment
+ ========================================================================== */
+
+pre.pm-pre,
+.pm-pre {
+ background: transparent !important;
+ border: none !important;
+ border-radius: 0 !important;
+ margin: 0 !important;
+ padding: 0 !important;
+}
+
+/* ASCII art tables inside terminals - no chrome, consistent monospace */
+pre.table-box {
+ background: transparent !important;
+ border: none !important;
+ border-radius: 0 !important;
+ margin: 0 !important;
+ padding: 0 !important;
+}
+
+pre.table-box,
+pre.table-box * {
+ font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas,
+ "DejaVu Sans Mono", monospace !important;
+ font-size: inherit !important;
+ font-weight: 400 !important;
+ font-style: normal !important;
+ font-variant: normal !important;
+ font-variant-ligatures: none !important;
+ font-feature-settings: normal !important;
+ font-stretch: normal !important;
+ letter-spacing: 0 !important;
+ word-spacing: 0 !important;
+ text-decoration: none !important;
+ text-transform: none !important;
+ -webkit-text-size-adjust: none !important;
+}
+
+/* ==========================================================================
+ Mobile Responsive
+ ========================================================================== */
+
+@media (max-width: 768px) {
+ /* Reset search bar to flow naturally in the compact docs header. */
+ site-search button {
+ position: static !important;
+ left: auto !important;
+ top: auto !important;
+ transform: none !important;
+ z-index: auto !important;
+ width: auto !important;
+ min-width: auto !important;
+ padding: 0.5rem !important;
+ }
+
+ site-search button span,
+ site-search button kbd {
+ display: none !important;
+ }
+
+ .hero {
+ text-align: center !important;
+ }
+
+ .hero h1 {
+ font-size: 2rem !important;
+ }
+
+ .hero .tagline {
+ font-size: 1rem !important;
+ margin: 0 auto !important;
+ }
+
+ .hero-command {
+ align-items: center;
+ }
+
+ .hero-command .command-box-wrapper {
+ justify-content: center;
+ }
+
+ .hero-command .install-selector {
+ max-width: 100%;
+ }
+
+ .hero-command .install-box {
+ flex-shrink: 0;
+ }
+
+ .hero-command .command-area {
+ font-size: 0.7rem;
+ padding: 0.5rem 0.75rem;
+ }
+}
+
+/* ==========================================================================
+ Overscroll Easter Egg
+ ========================================================================== */
+
+.overscroll-message {
+ position: fixed;
+ bottom: -60px;
+ left: 50%;
+ transform: translateX(-50%) translateY(0);
+ z-index: 9999;
+ pointer-events: none;
+ opacity: 0;
+ transition:
+ opacity 0.15s ease-out,
+ transform 0.15s ease-out;
+}
+
+.overscroll-message span {
+ display: inline-block;
+ padding: 0.6rem 1.5rem;
+ background: rgb(var(--sl-color-accent-rgb) / 0.12);
+ border: 1px solid rgb(var(--sl-color-accent-rgb) / 0.25);
+ border-radius: 24px;
+ color: rgba(255, 255, 255, 0.8);
+ font-size: 0.9rem;
+ font-weight: 500;
+ font-family: var(--sl-font);
+ backdrop-filter: blur(12px);
+ white-space: nowrap;
+}
+
+.overscroll-message code {
+ background: rgba(255, 255, 255, 0.1);
+ padding: 0.2em 0.5em;
+ border-radius: 6px;
+ font-family: var(--sl-font-mono);
+ font-size: 0.85em;
+ color: #9e86ff;
+ cursor: pointer;
+ pointer-events: auto;
+}
+
+.overscroll-message code:hover {
+ background: rgba(255, 255, 255, 0.18);
+}
+
+@media (max-width: 640px) {
+ .overscroll-message span {
+ white-space: normal;
+ text-align: center;
+ max-width: calc(100vw - 2rem);
+ font-size: 0.8rem;
+ padding: 0.5rem 1rem;
+ }
+}
diff --git a/apps/cli-docs/test/install-selector.test.mjs b/apps/cli-docs/test/install-selector.test.mjs
new file mode 100644
index 000000000..4b99c1e76
--- /dev/null
+++ b/apps/cli-docs/test/install-selector.test.mjs
@@ -0,0 +1,67 @@
+import assert from "node:assert/strict";
+import { readFileSync } from "node:fs";
+import { fileURLToPath } from "node:url";
+import { runInNewContext } from "node:vm";
+import test from "node:test";
+
+const component = readFileSync(fileURLToPath(new URL("../src/components/InstallSelector.astro", import.meta.url)), "utf8");
+const script = component.match(/
+