elastic / elastic/integrations

[New Integration] Cursor Analytics & Code Tracking API's

Open
#17,709 2 comments 0 reactions 1 assignee Claimed by @P1llus View on GitHub
New Integration Team:Integration-Experience
Dominant language
Handlebars
Stars
333
Forks
647
Avg merge
2d 17h
Merged PRs (30d)
225

Description

The data, field mapping, text etc was written by me, AI was only used to convert it to markdown.

The only open questions are:
1. For datastream 5, since the metrics are split up into so many small API's I currently added them under a single datastream, calling all API's in a single CEL interval, this could easily be changed into many smaller datastreams on request.
2. Event.category and event.type for each datastream is just a wild guess because there is nothing that really fits, any other suggestions on existing fields are welcome.
3. Handling CEL state cursor for datastream 5, which polls daily and uses full days in their endDate filter

All the datastreams includes all relevant details like field and ECS mapping, manifest.yml fields, pagination usage etc **(not AI generated)**.

> Addition to #17525 — adding datastreams for the [Code Tracking API](https://cursor.com/docs/account/teams/ai-code-tracking-api) and [Analytics API](https://cursor.com/docs/account/teams/analytics-api).

All APIs use **Basic Auth** with the API key as the username and empty password (for some reason..)

---

## Datastreams Overview

| # | Datastream | API Endpoint Base | Schedule |
|---|-----------|-------------------|----------|
| 1 | `code_tracking_commits` | `/analytics/ai-code/commits` | Daily |
| 2 | `code_change_metrics` | `/analytics/ai-code/changes` | Daily |
| 3 | `leaderboard` | `/analytics/team/leaderboard` | Daily |
| 4 | `bugbot` | `/analytics/team/bugbot` | Daily |
| 5 | `metrics` | Multiple `/analytics/team/*` endpoints | Daily |

---

## Datastream 1 — `code_tracking_commits`

### Request

```
GET https://api.cursor.com/analytics/ai-code/commits?startDate={initial_interval/cursor.timestamp}&endDate=now&page=1&pageSize={page_size}
Authorization: Basic
```

**Pagination:** Ceiling division of `totalCount / pageSize`, or paginate until `items` is empty.
**Cursor:** Use `interval` as the `startDate` parameter after initial_interval run, or "now + 1d". (Supports both 2025-01-15 and 2025-01-15T00:00:00Z)

### Configuration

Expand

| Parameter | Type | Default | Notes |
|-----------|------|---------|-------|
| `interval` | schedule | 1d | Run once per day |
| `initial_interval` | duration | 7 days | Max lookback per API rules |
| `api_key` | secret | — | Basic Auth username |
| `page_size` | number | 100 | Optional |

### Response Example

Expand

```json
{
"items": [
{
"commitHash": "a1b2c3d4",
"userId": "user_3k9x8q...",
"userEmail": "developer@company.com",
"repoName": "company/repo",
"branchName": "main",
"isPrimaryBranch": true,
"totalLinesAdded": 120,
"totalLinesDeleted": 30,
"tabLinesAdded": 50,
"tabLinesDeleted": 10,
"composerLinesAdded": 40,
"composerLinesDeleted": 5,
"nonAiLinesAdded": 30,
"nonAiLinesDeleted": 15,
"message": "Refactor: extract analytics client",
"commitTs": "2025-07-30T14:12:03.000Z",
"createdAt": "2025-07-30T14:12:30.000Z"
}
],
"totalCount": 42,
"page": 1,
"pageSize": 100
}
```

### ECS Field Mapping

Expand

| Source Field | Type | ECS / Target Field |
|---|---|---|
| — | — | `event.action` = `"code_changes_metrics"` |
| — | — | `event.category` = `["file"]` |
| — | — | `event.type` = `["info"]` |
| `commitTs` | date | `@timestamp` |
| `createdAt` | date | `event.created` |
| `userId` | string | `user.id` |
| `userEmail` | string | `user.name` |
| `message` | string | `message` |
| `commitHash` | string | `cursor.code_tracking_commits.commit_hash` |
| `repoName` | string | `cursor.code_tracking_commits.repo_name` |
| `branchName` | string | `cursor.code_tracking_commits.branch_name` |
| `isPrimaryBranch` | boolean | `cursor.code_tracking_commits.is_primary_branch` |
| `totalLinesAdded` | int | `cursor.code_tracking_commits.total_lines_added` |
| `totalLinesDeleted` | int | `cursor.code_tracking_commits.total_lines_deleted` |
| `tabLinesAdded` | int | `cursor.code_tracking_commits.tab_lines_added` |
| `tabLinesDeleted` | int | `cursor.code_tracking_commits.tab_lines_deleted` |
| `composerLinesAdded` | int | `cursor.code_tracking_commits.composer_lines_added` |
| `composerLinesDeleted` | int | `cursor.code_tracking_commits.composer_lines_deleted` |
| `nonAiLinesAdded` | int | `cursor.code_tracking_commits.non_ai_lines_added` |
| `nonAiLinesDeleted` | int | `cursor.code_tracking_commits.non_ai_lines_deleted` |

---

## Datastream 2 — `code_change_metrics`

### Request

```
GET https://api.cursor.com/analytics/ai-code/changes?startDate={initial_interval/cursor.interval}&endDate=now&page=1&pageSize={page_size}
Authorization: Basic
```

**Pagination:** Ceiling division of `totalCount / pageSize`, or paginate until `items` is empty.
**Cursor:** Use `interval` as the `startDate` parameter after initial_interval run, or "now + 1d". (Supports both 2025-01-15 and 2025-01-15T00:00:00Z)

### Configuration

Expand

| Parameter | Type | Default | Notes |
|-----------|------|---------|-------|
| `interval` | schedule | daily | 1d lookback |
| `initial_interval` | duration | 7 days | Max lookback per API rules |
| `api_key` | secret | — | Basic Auth username |
| `page_size` | number | 200 | Optional |

### Response Example

Expand

```json
{
"items": [
{
"changeId": "749356201",
"userId": "user_3k9x8q...",
"userEmail": "developer@company.com",
"source": "COMPOSER",
"model": null,
"totalLinesAdded": 18,
"totalLinesDeleted": 4,
"createdAt": "2025-07-30T15:10:12.000Z",
"metadata": [
{
"fileName": "src/analytics/report.ts",
"fileExtension": "ts",
"linesAdded": 12,
"linesDeleted": 3
},
{
"fileName": "src/analytics/ui.tsx",
"fileExtension": "tsx",
"linesAdded": 6,
"linesDeleted": 1
}
]
}
],
"totalCount": 128,
"page": 1,
"pageSize": 200
}
```

### ECS Field Mapping

> **Note:** The `metadata` array should be flattened — one document per `metadata` entry.

Expand

| Source Field | Type | ECS / Target Field |
|---|---|---|
| — | — | `event.action` = `"code_changes"` |
| — | — | `event.category` = `["file"]` |
| — | — | `event.type` = `["change", "info"]` |
| `createdAt` | date | `@timestamp` |
| `changeId` | string | `event.id` |
| `userId` | string | `user.id` |
| `userEmail` | string | `user.name` |
| `source` | string | `cursor.code_change_metrics.source` |
| `model` | string | `cursor.code_change_metrics.model` |
| `totalLinesAdded` | int | `cursor.code_change_metrics.total_lines_added` |
| `totalLinesDeleted` | int | `cursor.code_change_metrics.total_lines_deleted` |
| `metadata[].fileName` | string | `file.name` |
| `metadata[].fileExtension` | string | `file.extension` |
| `metadata[].linesAdded` | int | `cursor.code_change_metrics.lines_added` |
| `metadata[].linesDeleted` | int | `cursor.code_change_metrics.lines_deleted` |

---

## Datastream 3 — `leaderboard`

### Request

```
GET https://api.cursor.com/analytics/team/leaderboard?startDate={initial_interval/cursor.params.endDate}&endDate=now&page=1&pageSize={page_size}
Authorization: Basic
```

**Pagination:** Use `pagination.hasNextPage` / `pagination.totalPages` from the response.
**Cursor:** Use `cursor.params.endDate` as the `startDate` parameter after initial_interval run. (Supports shortcuts like `1d`, `7d`, `30d`, `90d`,etc.)

### Configuration

Expand

| Parameter | Type | Default | Notes |
|-----------|------|---------|-------|
| `interval` | schedule | 1d | 1d lookback |
| `initial_interval` | duration | 7 days | Max lookback per API rules |
| `api_key` | secret | — | Basic Auth username |
| `page_size` | number | 200 | Optional |

### Response Example

Expand

```json
{
"data": {
"tab_leaderboard": {
"data": [
{
"email": "alice@example.com",
"user_id": "user_abc123",
"profile_picture_url": "https://example.com/avatars/alice.jpg",
"total_accepts": 1334,
"total_lines_accepted": 3455,
"total_lines_suggested": 15307,
"line_acceptance_ratio": 0.2256519892590384,
"accept_ratio": 0.2330827067669173,
"rank": 1
},
{
"email": "bob@example.com",
"user_id": "user_def789",
"profile_picture_url": "https://example.com/avatars/bob.jpg",
"total_accepts": 796,
"total_lines_accepted": 2090,
"total_lines_suggested": 7689,
"line_acceptance_ratio": 0.2718168812589414,
"accept_ratio": 0.2731256599787746,
"rank": 2
}
],
"total_users": 142
},
"agent_leaderboard": {
"data": [
{
"email": "alice@example.com",
"user_id": "user_abc123",
"profile_picture_url": "https://example.com/avatars/alice.jpg",
"total_accepts": 914,
"total_lines_accepted": 65947,
"total_lines_suggested": 201467,
"line_acceptance_ratio": 0.3273465219182842,
"rank": 1
},
{
"email": "bob@example.com",
"user_id": "user_def789",
"profile_picture_url": "https://example.com/avatars/bob.jpg",
"total_accepts": 843,
"total_lines_accepted": 61709,
"total_lines_suggested": 51092,
"line_acceptance_ratio": 1.2077924536684573,
"rank": 2
}
],
"total_users": 142
}
},
"pagination": {
"page": 1,
"pageSize": 10,
"totalUsers": 142,
"totalPages": 15,
"hasNextPage": true,
"hasPreviousPage": false
},
"params": {
"metric": "leaderboard",
"teamId": 12345,
"startDate": "2025-01-01",
"endDate": "2025-01-31",
"page": 1,
"pageSize": 10
}
}
```

### Processing Note

The `data` object should be split on each root key (e.g. `tab_leaderboard`, `agent_leaderboard`). Each child `.data` array entry becomes its own document, with a custom field indicating the leaderboard type. The leaderboard type is reflected in `event.action`.

### ECS Field Mapping

Expand

| Source Field | Type | ECS / Target Field |
|---|---|---|
| — | — | `event.action` = `"leaderboard_tab"` \| `"leaderboard_agent"` (per leaderboard type) |
| — | — | `event.category` = `["session"]` |
| — | — | `event.type` = `["info"]` |
| `email` | string | `user.name` |
| `user_id` | string | `user.id` |
| `profile_picture_url` | string | `event.reference` |
| `total_accepts` | int | `cursor.leaderboard.total_accepts` |
| `total_lines_accepted` | int | `cursor.leaderboard.total_lines_accepted` |
| `total_lines_suggested` | int | `cursor.leaderboard.total_lines_suggested` |
| `line_acceptance_ratio` | float | `cursor.leaderboard.line_acceptance_ratio` |
| `accept_ratio` | float | `cursor.leaderboard.accept_ratio` |
| `rank` | int | `cursor.leaderboard.rank` |

---

## Datastream 4 — `bugbot`

### Request

```
# With optional repo and PR state filters:
GET https://api.cursor.com/analytics/team/bugbot?repo=github.com/acme/app&prState=merged&startDate={initial_interval/cursor.params.endDate}&endDate=now&page=1&pageSize={page_size}

# Without repo filter:
GET https://api.cursor.com/analytics/team/bugbot?startDate={initial_interval/cursor.params.endDate}&endDate=now&page=1&pageSize={page_size}

Authorization: Basic
```

**Pagination:** Use `pagination.hasNextPage` / `pagination.totalPages` from the response.
**Cursor:** Use `cursor.params.endDate` as the `startDate` parameter after initial_interval run. (Supports both 2025-01-15 and 2025-01-15T00:00:00Z)

### Configuration

Expand

| Parameter | Type | Default | Notes |
|-----------|------|---------|-------|
| `interval` | schedule | daily | 1d lookback |
| `initial_interval` | duration | 7 days | Max lookback per API rules |
| `api_key` | secret | — | Basic Auth username |
| `repository` | text | — | Optional. Filter by repo, e.g. `github.com/acme/app` |
| `pr_state` | text | `merged` | Allowed values: `merged`, `all` |
| `page_size` | number | 100 | Optional |

### Response Example

Expand

```json
{
"data": [
{
"repo": "github.com/acme/app",
"pr_number": 42,
"timestamp": "2025-01-21T00:00:00.000Z",
"reviews": 3,
"issues": {
"total": 5,
"by_severity": {
"high": 1,
"medium": 2,
"low": 2
}
},
"issues_resolved": {
"total": 2,
"by_severity": {
"high": 1,
"medium": 1,
"low": 0
}
}
}
],
"pagination": {
"page": 1,
"pageSize": 100,
"totalItems": 1,
"totalPages": 1,
"hasNextPage": false,
"hasPreviousPage": false
}
}
```

### ECS Field Mapping

Expand

| Source Field | Type | ECS / Target Field |
|---|---|---|
| — | — | `event.action` = `"bugbot"` |
| — | — | `event.category` = `["session"]` |
| — | — | `event.type` = `["info"]` |
| `timestamp` | date | `@timestamp` |
| `repo` | string | `cursor.bugbot.repo` |
| `pr_number` | int | `cursor.bugbot.pr_number` |
| `reviews` | int | `cursor.bugbot.reviews` |
| `issues.total` | int | `cursor.bugbot.issues.total` |
| `issues.by_severity.high` | int | `cursor.bugbot.issues.high` |
| `issues.by_severity.medium` | int | `cursor.bugbot.issues.medium` |
| `issues.by_severity.low` | int | `cursor.bugbot.issues.low` |
| `issues_resolved.total` | int | `cursor.bugbot.issues_resolved.total` |
| `issues_resolved.by_severity.high` | int | `cursor.bugbot.issues_resolved.high` |
| `issues_resolved.by_severity.medium` | int | `cursor.bugbot.issues_resolved.medium` |
| `issues_resolved.by_severity.low` | int | `cursor.bugbot.issues_resolved.low` |

---

## Datastream 5 — `metrics`

> All small aggregated team metrics are collected under a single datastream, differentiated by `event.action`. These endpoints return daily aggregates and do not support pagination.

### Endpoints

| `event.action` | Endpoint |
|---|---|
| `agent_edits_metrics` | `GET /analytics/team/agent-edits` |
| `agent_tabs_metrics` | `GET /analytics/team/tabs` |
| `agent_dau_metrics` | `GET /analytics/team/dau` |
| `agent_client_versions_metrics` | `GET /analytics/team/client-versions` |
| `agent_top_file_extensions_metrics` | `GET /analytics/team/top-file-extensions` |
| `agent_mcp_metrics` | `GET /analytics/team/mcp` |
| `agent_commands_metrics` | `GET /analytics/team/commands` |
| `agent_plans_metrics` | `GET /analytics/team/plans` |
| `agent_ask_mode_metrics` | `GET /analytics/team/ask-mode` |

All endpoints accept `?startDate={initial_interval/cursor.params.endDate}&endDate=now` and use `Authorization: Basic `.

**Pagination:** No pagination provided, aggregates per day.
**Cursor:** Use `cursor.params.endDate` as the `startDate` parameter after initial_interval run. (Supports both 2025-01-15 and 2025-01-15T00:00:00Z)
**NOTE:** An issue with the cursor is that if we run the interval for "now -1d" at the start of a day, we might end up with missing a day?

### Configuration

Expand

| Parameter | Type | Default | Notes |
|-----------|------|---------|-------|
| `interval` | schedule | daily | 1d lookback |
| `initial_interval` | duration | 7 days | Max lookback per API rules |
| `api_key` | secret | — | Basic Auth username |

---

### 5a — Agent Edits (`agent_edits_metrics`)

Response Example

```json
{
"data": [
{
"event_date": "2025-01-15",
"total_suggested_diffs": 145,
"total_accepted_diffs": 98,
"total_rejected_diffs": 47,
"total_green_lines_accepted": 820,
"total_red_lines_accepted": 160,
"total_green_lines_rejected": 210,
"total_red_lines_rejected": 60,
"total_green_lines_suggested": 1030,
"total_red_lines_suggested": 220,
"total_lines_suggested": 1250,
"total_lines_accepted": 980
}
]
}
```

ECS Field Mapping

| Source Field | Type | ECS / Target Field |
|---|---|---|
| — | — | `event.action` = `"agent_edits_metrics"` |
| — | — | `event.category` = `["host"]` |
| — | — | `event.type` = `["info"]` |
| `event_date` | date | `@timestamp` (time set to `00:00:00`) |
| `total_suggested_diffs` | int | `cursor.metrics.total_suggested_diffs` |
| `total_accepted_diffs` | int | `cursor.metrics.total_accepted_diffs` |
| `total_rejected_diffs` | int | `cursor.metrics.total_rejected_diffs` |
| `total_green_lines_accepted` | int | `cursor.metrics.total_green_lines_accepted` |
| `total_red_lines_accepted` | int | `cursor.metrics.total_red_lines_accepted` |
| `total_green_lines_rejected` | int | `cursor.metrics.total_green_lines_rejected` |
| `total_red_lines_rejected` | int | `cursor.metrics.total_red_lines_rejected` |
| `total_green_lines_suggested` | int | `cursor.metrics.total_green_lines_suggested` |
| `total_red_lines_suggested` | int | `cursor.metrics.total_red_lines_suggested` |
| `total_lines_suggested` | int | `cursor.metrics.total_lines_suggested` |
| `total_lines_accepted` | int | `cursor.metrics.total_lines_accepted` |

---

### 5b — Tabs (`agent_tabs_metrics`)

Same field structure as Agent Edits. `event.action` = `"agent_tabs_metrics"`.

Response Example

```json
{
"data": [
{
"event_date": "2025-01-15",
"total_suggestions": 5420,
"total_accepts": 3210,
"total_rejects": 2210,
"total_green_lines_accepted": 4120,
"total_red_lines_accepted": 2000,
"total_green_lines_rejected": 1480,
"total_red_lines_rejected": 730,
"total_green_lines_suggested": 5600,
"total_red_lines_suggested": 2740,
"total_lines_suggested": 8340,
"total_lines_accepted": 6120
}
]
}
```

ECS Field Mapping

| Source Field | Type | ECS / Target Field |
|---|---|---|
| — | — | `event.action` = `"agent_tabs_metrics"` |
| — | — | `event.category` = `["host"]` |
| — | — | `event.type` = `["info"]` |
| `event_date` | date | `@timestamp` (time set to `00:00:00`) |
| `total_suggestions` | int | `cursor.metrics.total_suggestions` |
| `total_accepts` | int | `cursor.metrics.total_accepts` |
| `total_rejects` | int | `cursor.metrics.total_rejects` |
| `total_green_lines_accepted` | int | `cursor.metrics.total_green_lines_accepted` |
| `total_red_lines_accepted` | int | `cursor.metrics.total_red_lines_accepted` |
| `total_green_lines_rejected` | int | `cursor.metrics.total_green_lines_rejected` |
| `total_red_lines_rejected` | int | `cursor.metrics.total_red_lines_rejected` |
| `total_green_lines_suggested` | int | `cursor.metrics.total_green_lines_suggested` |
| `total_red_lines_suggested` | int | `cursor.metrics.total_red_lines_suggested` |
| `total_lines_suggested` | int | `cursor.metrics.total_lines_suggested` |
| `total_lines_accepted` | int | `cursor.metrics.total_lines_accepted` |

---

### 5c — DAU (`agent_dau_metrics`)

Response Example

```json
{
"data": [
{
"date": "2025-01-15",
"dau": 42,
"cli_dau": 5,
"cloud_agent_dau": 37,
"bugbot_dau": 10
}
]
}
```

> **Note:** This endpoint uses `date` instead of `event_date` as the timestamp field.

ECS Field Mapping

| Source Field | Type | ECS / Target Field |
|---|---|---|
| — | — | `event.action` = `"agent_dau_metrics"` |
| — | — | `event.category` = `["host"]` |
| — | — | `event.type` = `["info"]` |
| `date` | date | `@timestamp` (time set to `00:00:00`) |
| `dau` | int | `cursor.metrics.dau` |
| `cli_dau` | int | `cursor.metrics.cli_dau` |
| `cloud_agent_dau` | int | `cursor.metrics.cloud_agent_dau` |
| `bugbot_dau` | int | `cursor.metrics.bugbot_dau` |

---

### 5d — Client Versions (`agent_client_versions_metrics`)

Response Example

```json
{
"data": [
{
"event_date": "2025-01-01",
"client_version": "0.42.3",
"user_count": 35,
"percentage": 0.833
}
]
}
```

ECS Field Mapping

| Source Field | Type | ECS / Target Field |
|---|---|---|
| — | — | `event.action` = `"agent_client_versions_metrics"` |
| — | — | `event.category` = `["host"]` |
| — | — | `event.type` = `["info"]` |
| `event_date` | date | `@timestamp` (time set to `00:00:00`) |
| `client_version` | string | `cursor.metrics.client_version` |
| `user_count` | int | `cursor.metrics.user_count` |
| `percentage` | float | `cursor.metrics.percentage` |

---

### 5e — Top File Extensions (`agent_top_file_extensions_metrics`)

Response Example

```json
{
"data": [
{
"event_date": "2025-01-15",
"file_extension": "tsx",
"total_files": 156,
"total_accepts": 98,
"total_rejects": 45,
"total_lines_suggested": 3230,
"total_lines_accepted": 2340,
"total_lines_rejected": 890
}
]
}
```

ECS Field Mapping

| Source Field | Type | ECS / Target Field |
|---|---|---|
| — | — | `event.action` = `"agent_top_file_extensions_metrics"` |
| — | — | `event.category` = `["file"]` |
| — | — | `event.type` = `["info"]` |
| `event_date` | date | `@timestamp` (time set to `00:00:00`) |
| `file_extension` | string | `file.extension` |
| `total_files` | int | `cursor.metrics.total_files` |
| `total_accepts` | int | `cursor.metrics.total_accepts` |
| `total_rejects` | int | `cursor.metrics.total_rejects` |
| `total_lines_suggested` | int | `cursor.metrics.total_lines_suggested` |
| `total_lines_accepted` | int | `cursor.metrics.total_lines_accepted` |
| `total_lines_rejected` | int | `cursor.metrics.total_lines_rejected` |

---

### 5f — MCP (`agent_mcp_metrics`)

Response Example

```json
{
"data": [
{
"event_date": "2025-01-15",
"tool_name": "read_file",
"mcp_server_name": "filesystem",
"usage": 245
}
]
}
```

ECS Field Mapping

| Source Field | Type | ECS / Target Field |
|---|---|---|
| — | — | `event.action` = `"agent_mcp_metrics"` |
| — | — | `event.category` = `["process"]` |
| — | — | `event.type` = `["info"]` |
| `event_date` | date | `@timestamp` (time set to `00:00:00`) |
| `tool_name` | string | `cursor.metrics.tool_name` |
| `mcp_server_name` | string | `cursor.metrics.mcp_server_name` |
| `usage` | int | `cursor.metrics.usage` |

---

### 5g — Commands (`agent_commands_metrics`)

Response Example

```json
{
"data": [
{
"event_date": "2025-01-15",
"command_name": "explain",
"usage": 89
}
]
}
```

ECS Field Mapping

| Source Field | Type | ECS / Target Field |
|---|---|---|
| — | — | `event.action` = `"agent_commands_metrics"` |
| — | — | `event.category` = `["process"]` |
| — | — | `event.type` = `["info"]` |
| `event_date` | date | `@timestamp` (time set to `00:00:00`) |
| `command_name` | string | `cursor.metrics.command_name` |
| `usage` | int | `cursor.metrics.usage` |

---

### 5h — Plans (`agent_plans_metrics`)

Response Example

```json
{
"data": [
{
"event_date": "2025-01-15",
"model": "claude-sonnet-4.5",
"usage": 156
}
]
}
```

ECS Field Mapping

| Source Field | Type | ECS / Target Field |
|---|---|---|
| — | — | `event.action` = `"agent_plans_metrics"` |
| — | — | `event.category` = `["process"]` |
| — | — | `event.type` = `["info"]` |
| `event_date` | date | `@timestamp` (time set to `00:00:00`) |
| `model` | string | `cursor.metrics.model` |
| `usage` | int | `cursor.metrics.usage` |

---

### 5i — Ask Mode (`agent_ask_mode_metrics`)

Response Example

```json
{
"data": [
{
"event_date": "2025-01-15",
"model": "claude-sonnet-4.5",
"usage": 203
}
]
}
```

ECS Field Mapping

| Source Field | Type | ECS / Target Field |
|---|---|---|
| — | — | `event.action` = `"agent_ask_mode_metrics"` |
| — | — | `event.category` = `["process"]` |
| — | — | `event.type` = `["info"]` |
| `event_date` | date | `@timestamp` (time set to `00:00:00`) |
| `model` | string | `cursor.metrics.model` |
| `usage` | int | `cursor.metrics.usage` |

---

## Dashboards and Visualizations

### Dashboard 1 — AI Code Impact

> Primary datastreams: `code_tracking_commits`, `code_change_metrics`

High-level view of how much code AI is generating, where, and how it compares to human-written code.

| # | Visualization | Type | Fields Used | Filters / Notes |
|---|---|---|---|---|
| 1 | AI vs Non-AI Lines Over Time | Line chart | `@timestamp`, `cursor.code_tracking_commits.tab_lines_added`, `cursor.code_tracking_commits.composer_lines_added`, `cursor.code_tracking_commits.non_ai_lines_added` | Stacked lines; one series per source. Filter `cursor.code_tracking_commits.is_primary_branch: true` to focus on main branches |
| 2 | AI Code Proportion | Donut | `cursor.code_tracking_commits.tab_lines_added`, `cursor.code_tracking_commits.composer_lines_added`, `cursor.code_tracking_commits.non_ai_lines_added` | Three slices: Tab, Composer, Non-AI. Sum over selected time range |
| 3 | Top Repositories by AI Lines | Horizontal bar | `cursor.code_tracking_commits.repo_name`, sum of `cursor.code_tracking_commits.total_lines_added` | Top N repos. Optionally filter `cursor.code_tracking_commits.is_primary_branch: true` |
| 4 | Code Changes by Source | Donut | `cursor.code_change_metrics.source` (count) | One slice per source value (COMPOSER, TAB, etc.) |
| 5 | Top File Extensions by Lines Changed | Horizontal bar | `file.extension`, sum of `cursor.code_change_metrics.lines_added` | From `code_change_metrics`; top N extensions |
| 6 | Recent Commits | Table | `@timestamp`, `user.name`, `cursor.code_tracking_commits.repo_name`, `cursor.code_tracking_commits.branch_name`, `cursor.code_tracking_commits.total_lines_added`, `cursor.code_tracking_commits.total_lines_deleted`, `message` | Sorted by `@timestamp` desc; paginated |
| 7 | Total AI Lines Added | Metric tile | Sum of `cursor.code_tracking_commits.tab_lines_added` + `cursor.code_tracking_commits.composer_lines_added` | Single number over selected range |
| 8 | Total Commits | Metric tile | Count of `code_tracking_commits` documents | — |

---

### Dashboard 2 — Developer Leaderboard

> Primary datastream: `leaderboard`

Rankings and acceptance metrics per developer across Tab and Agent leaderboards.

| # | Visualization | Type | Fields Used | Filters / Notes |
|---|---|---|---|---|
| 1 | Tab Leaderboard | Table | `cursor.leaderboard.rank`, `user.name`, `cursor.leaderboard.total_accepts`, `cursor.leaderboard.total_lines_accepted`, `cursor.leaderboard.total_lines_suggested`, `cursor.leaderboard.accept_ratio`, `cursor.leaderboard.line_acceptance_ratio` | Filter `event.action: "leaderboard_tab"`. Sort by `cursor.leaderboard.rank` asc |
| 2 | Agent Leaderboard | Table | Same fields as above (except `accept_ratio` which is absent for agent) | Filter `event.action: "leaderboard_agent"`. Sort by `cursor.leaderboard.rank` asc |
| 3 | Top Developers by Accepts | Horizontal bar | `user.name`, `cursor.leaderboard.total_accepts` | Filter by leaderboard type via `event.action`. Top N |
| 4 | Team Acceptance Ratio Distribution | Histogram | `cursor.leaderboard.line_acceptance_ratio` | Bucket developers by acceptance ratio to show distribution |
| 5 | Total Team Accepts | Metric tile | Sum of `cursor.leaderboard.total_accepts` | Filter by `event.action` to choose Tab or Agent |
| 6 | Avg Line Acceptance Ratio | Metric tile | Avg of `cursor.leaderboard.line_acceptance_ratio` | — |

---

### Dashboard 3 — Team Usage & Adoption

> Primary datastream: `metrics` (sub-endpoints 5a–5i)

Aggregate team-level usage trends including DAU, suggestions, client versions, and model/tool usage.

| # | Visualization | Type | Fields Used | Filters / Notes |
|---|---|---|---|---|
| 1 | Daily Active Users Over Time | Line chart | `@timestamp`, `cursor.metrics.dau`, `cursor.metrics.cli_dau`, `cursor.metrics.cloud_agent_dau`, `cursor.metrics.bugbot_dau` | Filter `event.action: "agent_dau_metrics"`. Stacked or multi-line |
| 2 | Suggestions vs Accepts vs Rejects | Stacked area | `@timestamp`, `cursor.metrics.total_accepts`, `cursor.metrics.total_rejects` | Filter `event.action: "agent_tabs_metrics"` |
| 3 | Agent Edit Acceptance Rate Over Time | Line chart | `@timestamp`, computed ratio of `cursor.metrics.total_accepted_diffs` / `cursor.metrics.total_suggested_diffs` | Filter `event.action: "agent_edits_metrics"` |
| 4 | Client Version Distribution | Donut | `cursor.metrics.client_version`, `cursor.metrics.user_count` | Filter `event.action: "agent_client_versions_metrics"`. Latest `@timestamp` only for point-in-time view |
| 5 | Top File Extensions | Horizontal bar | `file.extension`, `cursor.metrics.total_lines_accepted` | Filter `event.action: "agent_top_file_extensions_metrics"` |
| 6 | MCP Tool Usage | Horizontal bar | `cursor.metrics.tool_name`, `cursor.metrics.mcp_server_name`, `cursor.metrics.usage` | Filter `event.action: "agent_mcp_metrics"`. Group by server + tool |
| 7 | Command Usage | Horizontal bar | `cursor.metrics.command_name`, `cursor.metrics.usage` | Filter `event.action: "agent_commands_metrics"` |
| 8 | Model Usage — Plans vs Ask Mode | Stacked bar | `cursor.metrics.model`, `cursor.metrics.usage` | Side-by-side or stacked; filter `event.action` = `"agent_plans_metrics"` or `"agent_ask_mode_metrics"` |
| 9 | Lines Suggested vs Accepted (Tabs) | Metric tiles (pair) | `cursor.metrics.total_lines_suggested`, `cursor.metrics.total_lines_accepted` | Filter `event.action: "agent_tabs_metrics"` |
| 10 | Total DAU (Latest) | Metric tile | `cursor.metrics.dau` | Filter `event.action: "agent_dau_metrics"`, latest `@timestamp` |

---

### Dashboard 4 — BugBot Code Review

> Primary datastream: `bugbot`

PR review activity, issues found, severity breakdown, and resolution rates.

| # | Visualization | Type | Fields Used | Filters / Notes |
|---|---|---|---|---|
| 1 | Issues Found Over Time | Line chart | `@timestamp`, `cursor.bugbot.issues.total` | — |
| 2 | Issues by Severity | Stacked bar | `@timestamp`, `cursor.bugbot.issues.high`, `cursor.bugbot.issues.medium`, `cursor.bugbot.issues.low` | One color per severity |
| 3 | Found vs Resolved | Grouped bar | `cursor.bugbot.issues.total`, `cursor.bugbot.issues_resolved.total` | Paired bars per time bucket |
| 4 | Severity Distribution (All Time) | Donut | Sum of `cursor.bugbot.issues.high`, `.medium`, `.low` | Three slices |
| 5 | Resolution Rate | Metric tile | Computed `cursor.bugbot.issues_resolved.total` / `cursor.bugbot.issues.total` | Percentage display |
| 6 | PR Review Detail | Table | `@timestamp`, `cursor.bugbot.repo`, `cursor.bugbot.pr_number`, `cursor.bugbot.reviews`, `cursor.bugbot.issues.total`, `cursor.bugbot.issues_resolved.total` | Sorted by `@timestamp` desc. Optional filter by `cursor.bugbot.repo` |
| 7 | Top Repos by Issues Found | Horizontal bar | `cursor.bugbot.repo`, sum of `cursor.bugbot.issues.total` | Top N repos |
| 8 | Reviews Per PR Over Time | Line chart | `@timestamp`, avg of `cursor.bugbot.reviews` | Shows review activity trend |

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.