Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
43ec836
feat(replay): Add replay list and view commands
dcramer May 1, 2026
5d25a37
fix(replay): Address replay validation drift
dcramer May 1, 2026
1d75923
feat(replay): Improve replay lookup and linkage
dcramer May 1, 2026
5a869b4
ref(replay): Simplify replay schema typing
dcramer May 1, 2026
6fbefb5
feat(replay): Expand replay querying and inspection
dcramer May 1, 2026
0a182b2
fix(replay): Normalize replay field fallbacks
dcramer May 1, 2026
663d594
ref(replay): Share replay environment parsing
dcramer May 1, 2026
2b1f63b
fix(replay): Address replay review feedback
dcramer May 1, 2026
1f7cb8b
fix(replay): Tighten replay follow-up behavior
dcramer May 1, 2026
68bd9ed
fix(replay): Clean up replay review feedback
dcramer May 1, 2026
eb5bf8a
fix(replay): Correct replay field and activity timing
dcramer May 1, 2026
90a6a1f
fix(replay): Tighten replay explore field handling
dcramer May 1, 2026
bcc5540
fix(replay): Separate replay aliases from convenience fields
dcramer May 1, 2026
cf62b16
fix(replay): Validate replay IDs in parsed URLs
dcramer May 1, 2026
4d26542
fix(replay): Tighten replay URL and hint defaults
dcramer May 1, 2026
39230db
fix(replay): Preserve org context for replay listing URLs
dcramer May 1, 2026
8d1e027
fix(replay): Reuse the shared replay list period default
dcramer May 1, 2026
e771a23
fix(replay): Format replay URL parser test coverage
dcramer May 1, 2026
6c7e2be
fix(replay): Normalize replay query and ID handling
dcramer May 1, 2026
3b36311
fix(replay): Normalize replay URLs and list fields
dcramer May 1, 2026
9ce3297
fix(replay): Reject extra replay view args
dcramer May 1, 2026
71606e7
ref(replay): consolidate replay-duration and replay-id into shared ut…
BYK May 2, 2026
d598cb5
ref(replay): extract formatting helpers and address review findings
BYK May 2, 2026
dea9eb4
fix(replay): address review bot findings
BYK May 2, 2026
d505e54
ref(explore): centralize replay dataset branching into resolveDataset…
BYK May 2, 2026
60f66a4
docs: add missing JSDoc to replay search, explore, and view internals
BYK May 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/src/content/docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ cli/
│ │ ├── org/ # list, view
│ │ ├── project/ # create, delete, list, view
│ │ ├── release/ # list, view, create, finalize, delete, deploy, deploys, set-commits, propose-version
│ │ ├── replay/ # list, view
│ │ ├── repo/ # list
│ │ ├── sourcemap/ # inject, upload
│ │ ├── span/ # list, view
Expand Down
38 changes: 38 additions & 0 deletions docs/src/fragments/commands/replay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@

## 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
```
9 changes: 9 additions & 0 deletions plugins/sentry-cli/skills/sentry-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,15 @@ Manage Sentry dashboards

→ Full flags and examples: `references/dashboard.md`

### Replay

Search and inspect Session Replays

- `sentry replay list <org/project>` — List recent Session Replays
- `sentry replay view <replay-id-or-url...>` — View a Session Replay

→ Full flags and examples: `references/replay.md`

### Release

Work with Sentry releases
Expand Down
3 changes: 2 additions & 1 deletion plugins/sentry-cli/skills/sentry-cli/references/explore.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ Query aggregate event data (Explore)

**Flags:**
- `-F, --field <value>... - API field or aggregate (repeatable). E.g., title, "count()", "p50(transaction.duration)"`
- `-d, --dataset <value> - Dataset to query (errors, spans, metrics, logs) - (default: "errors")`
- `-d, --dataset <value> - Dataset to query (errors, spans, metrics, logs, replays) - (default: "errors")`
- `-q, --query <value> - Search query (Sentry search syntax)`
- `-s, --sort <value> - Sort field (prefix with - for desc, e.g., "-count()")`
- `-e, --environment <value>... - Replay environment filter for --dataset replays (repeatable, comma-separated)`
- `-n, --limit <value> - Number of rows (1-1000) - (default: "25")`
- `-t, --period <value> - Time range: "7d", "2026-04-01..2026-05-01", ">=2026-04-01" - (default: "24h")`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
Expand Down
148 changes: 148 additions & 0 deletions plugins/sentry-cli/skills/sentry-cli/references/replay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
---
name: sentry-cli-replay
version: 0.32.0-dev.0
description: Search and inspect Session Replays
requires:
bins: ["sentry"]
auth: true
---

# Replay Commands

Search and inspect Session Replays

### `sentry replay list <org/project>`

List recent Session Replays

**Flags:**
- `-n, --limit <value> - Number of replays (1-1000) - (default: "25")`
- `-q, --query <value> - Search query (Sentry replay search syntax)`
- `-e, --environment <value>... - Filter by environment (repeatable, comma-separated)`
- `-s, --sort <value> - Sort by: date, oldest, duration, errors, activity, or a raw replay sort field - (default: "date")`
- `-t, --period <value> - Time range: "7d", "2026-04-01..2026-05-01", ">=2026-04-01" - (default: "7d")`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `-c, --cursor <value> - Navigate pages: "next", "prev", "first" (or raw cursor string)`

**JSON Fields** (use `--json --fields` to select specific fields):

| Field | Type | Description |
|-------|------|-------------|
| `activity` | number \| null | Replay activity score |
| `browser` | object \| null | Browser metadata |
| `count_dead_clicks` | number \| null | Dead click count |
| `count_errors` | number \| null | Associated error count |
| `count_infos` | number \| null | Info event count |
| `count_rage_clicks` | number \| null | Rage click count |
| `count_segments` | number \| null | Recording segment count |
| `count_urls` | number \| null | Visited URL count |
| `count_warnings` | number \| null | Warning event count |
| `device` | object \| null | Device metadata |
| `dist` | string \| null | Distribution |
| `duration` | number \| null | Replay duration in seconds |
| `environment` | string \| null | Environment |
| `error_ids` | array | Linked error IDs |
| `finished_at` | string \| null | Replay finish timestamp |
| `has_viewed` | boolean \| null | Whether the current user has viewed the replay |
| `id` | string | Replay ID |
| `info_ids` | array | Linked info event IDs |
| `is_archived` | boolean \| null | Archived flag |
| `os` | object \| null | Operating system metadata |
| `ota_updates` | object \| null | OTA update metadata |
| `platform` | string \| null | Platform |
| `project_id` | string \| null | Numeric project ID |
| `releases` | array | Associated releases |
| `sdk` | object \| null | SDK metadata |
| `started_at` | string \| null | Replay start timestamp |
| `tags` | unknown | Replay tags |
| `trace_ids` | array | Linked trace IDs |
| `urls` | array | Visited URLs |
| `user` | object \| null | User metadata |
| `warning_ids` | array | Linked warning event IDs |

**Examples:**

```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
```

### `sentry replay view <replay-id-or-url...>`

View a Session Replay

**Flags:**
- `-w, --web - Open in browser`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`

**JSON Fields** (use `--json --fields` to select specific fields):

| Field | Type | Description |
|-------|------|-------------|
| `activity` | array | Summarized replay activity |
| `browser` | object \| null | Browser metadata |
| `count_dead_clicks` | number \| null | Dead click count |
| `count_errors` | number \| null | Associated error count |
| `count_infos` | number \| null | Info event count |
| `count_rage_clicks` | number \| null | Rage click count |
| `count_segments` | number \| null | Recording segment count |
| `count_urls` | number \| null | Visited URL count |
| `count_warnings` | number \| null | Warning event count |
| `device` | object \| null | Device metadata |
| `dist` | string \| null | Distribution |
| `duration` | number \| null | Replay duration in seconds |
| `environment` | string \| null | Environment |
| `error_ids` | array | Linked error IDs |
| `finished_at` | string \| null | Replay finish timestamp |
| `has_viewed` | boolean \| null | Whether the current user has viewed the replay |
| `id` | string | Replay ID |
| `info_ids` | array | Linked info event IDs |
| `is_archived` | boolean \| null | Archived flag |
| `os` | object \| null | Operating system metadata |
| `ota_updates` | object \| null | OTA update metadata |
| `platform` | string \| null | Platform |
| `project_id` | string \| null | Numeric project ID |
| `releases` | array | Associated releases |
| `sdk` | object \| null | SDK metadata |
| `started_at` | string \| null | Replay start timestamp |
| `tags` | unknown | Replay tags |
| `trace_ids` | array | Linked trace IDs |
| `urls` | array | Visited URLs |
| `user` | object \| null | User metadata |
| `warning_ids` | array | Linked warning event IDs |
| `clicks` | array | Replay click summaries |
| `replay_type` | string \| null | Replay type |
| `org` | string | Organization slug |
| `relatedIssues` | array | Replay-related issues |
| `relatedTraces` | array | Replay-related traces |

**Examples:**

```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
```

All commands also support `--json`, `--fields`, `--help`, `--log-level`, and `--verbose` flags.
6 changes: 6 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import { projectRoute } from "./commands/project/index.js";
import { listCommand as projectListCommand } from "./commands/project/list.js";
import { releaseRoute } from "./commands/release/index.js";
import { listCommand as releaseListCommand } from "./commands/release/list.js";
import { replayRoute } from "./commands/replay/index.js";
import { listCommand as replayListCommand } from "./commands/replay/list.js";
import { repoRoute } from "./commands/repo/index.js";
import { listCommand as repoListCommand } from "./commands/repo/list.js";
import { schemaCommand } from "./commands/schema.js";
Expand Down Expand Up @@ -70,6 +72,7 @@ const PLURAL_TO_SINGULAR: Record<string, string> = {
repos: "repo",
teams: "team",
logs: "log",
replays: "replay",

spans: "span",
traces: "trace",
Expand All @@ -85,6 +88,7 @@ export const routes = buildRouteMap({
dashboard: dashboardRoute,
org: orgRoute,
project: projectRoute,
replay: replayRoute,
release: releaseRoute,
repo: repoRoute,
team: teamRoute,
Expand All @@ -105,6 +109,7 @@ export const routes = buildRouteMap({
issues: issueListCommand,
orgs: orgListCommand,
projects: projectListCommand,
replays: replayListCommand,
releases: releaseListCommand,
repos: repoListCommand,
teams: teamListCommand,
Expand All @@ -126,6 +131,7 @@ export const routes = buildRouteMap({
issues: true,
orgs: true,
projects: true,
replays: true,
releases: true,
repos: true,
teams: true,
Expand Down
42 changes: 35 additions & 7 deletions src/commands/event/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
} from "../../lib/list-command.js";
import { logger } from "../../lib/logger.js";
import { resolveEffectiveOrg } from "../../lib/region.js";
import { getReplayIdFromEvent } from "../../lib/replay-search.js";
import {
resolveOrg,
resolveOrgAndProject,
Expand Down Expand Up @@ -148,6 +149,22 @@ export function jsonTransformEventView(
}
return data.events.map(transform);
}

/**
* Build a CLI-native replay hint when the event is linked to a replay.
*/
function replayHint(org: string, event: SentryEvent): string | undefined {
const replayId = getReplayIdFromEvent(event);
return replayId
? `Related replay: sentry replay view ${org}/${replayId}`
: undefined;
}

function joinHintParts(parts: Array<string | undefined>): string | undefined {
const hints = parts.filter((part): part is string => Boolean(part));
return hints.length > 0 ? hints.join(" | ") : undefined;
}
Comment thread
cursor[bot] marked this conversation as resolved.

/** Usage hint for ContextError messages */
const USAGE_HINT = "sentry event view <org>/<project> <event-id>";

Expand Down Expand Up @@ -417,8 +434,9 @@ type ResolveTargetOptions = {
*
* Handles all target types (explicit, search, org-all, auto-detect)
* including cross-project fallback via the eventids endpoint.
*
* @internal Exported for testing
*/
/** @internal Exported for testing */
export async function resolveEventTarget(
options: ResolveTargetOptions
): Promise<ResolvedEventTarget | null> {
Expand Down Expand Up @@ -470,8 +488,9 @@ export async function resolveEventTarget(
* Throws a ContextError if the event is not found in the given org, with a
* message that names the org so the error is not misleading.
* Propagates auth/network errors from resolveEventInOrg.
*
* @internal Exported for testing
*/
/** @internal Exported for testing */
export async function resolveOrgAllTarget(
org: string,
eventId: string,
Expand All @@ -497,8 +516,9 @@ export async function resolveOrgAllTarget(
/**
* Resolve target via auto-detect cascade, falling back to cross-project
* event search across all accessible orgs.
*
* @internal Exported for testing
*/
/** @internal Exported for testing */
export async function resolveAutoDetectTarget(
eventId: string,
cwd: string
Expand Down Expand Up @@ -987,7 +1007,12 @@ export const viewCommand = buildCommand({
events: [issueShortcut.data],
requestedCount: 1,
});
return { hint: issueShortcut.hint };
return {
hint: joinHintParts([
issueShortcut.hint,
replayHint(issueShortcut.org, issueShortcut.data.event),
]),
};
Comment thread
cursor[bot] marked this conversation as resolved.
}

// Validate + attempt recovery. `skipValidation` is true when the ID is
Expand Down Expand Up @@ -1043,9 +1068,12 @@ export const viewCommand = buildCommand({
requestedCount: allEventIds.length,
});
return {
hint: target.detectedFrom
? `Detected from ${target.detectedFrom}`
: undefined,
hint: joinHintParts([
target.detectedFrom
? `Detected from ${target.detectedFrom}`
: undefined,
fetchedEvents[0] ? replayHint(target.org, fetchedEvents[0]) : undefined,
]),
};
},
});
Loading
Loading