Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"@biomejs/biome": "2.3.8",
"@clack/prompts": "^0.11.0",
"@mastra/client-js": "^1.4.0",
"@sentry/api": "^0.133.0",
"@sentry/api": "^0.141.0",
"@sentry/node-core": "10.50.0",
"@sentry/sqlish": "^1.0.0",
"@stricli/auto-complete": "^1.2.4",
Expand Down
2 changes: 1 addition & 1 deletion plugins/sentry-cli/skills/sentry-cli/references/event.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ List events for an issue
| `platform` | string \| null | Platform (python, javascript, etc.) |
| `dateCreated` | string | ISO 8601 creation timestamp |
| `crashFile` | string \| null | Crash file URL |
| `metadata` | unknown \| null | Event metadata |
| `metadata` | object \| null | Event metadata |

**Examples:**

Expand Down
12 changes: 6 additions & 6 deletions plugins/sentry-cli/skills/sentry-cli/references/issue.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@ List issues in a project
| `culprit` | string | Culprit string |
| `count` | string | Total event count |
| `userCount` | number | Number of affected users |
| `firstSeen` | string \| null | First occurrence (ISO 8601) |
| `lastSeen` | string \| null | Most recent occurrence (ISO 8601) |
| `firstSeen` | string | First occurrence (ISO 8601) |
| `lastSeen` | string | Most recent occurrence (ISO 8601) |
| `level` | string | Severity level |
| `status` | string | Issue status |
| `priority` | string | Triage priority |
| `platform` | string | Platform |
| `permalink` | string | URL to the issue in Sentry |
| `project` | object | Project info |
| `metadata` | object | Issue metadata |
| `assignedTo` | unknown \| null | Assigned user or team |
| `assignedTo` | object \| null | Assigned user or team |
| `priority` | string | Triage priority |
| `platform` | string | Platform |
| `substatus` | string \| null | Issue substatus |
| `isUnhandled` | boolean | Whether the issue is unhandled |
| `seerFixabilityScore` | number \| null | Seer AI fixability score (0-1) |
Expand Down Expand Up @@ -109,7 +109,7 @@ List events for a specific issue
| `platform` | string \| null | Platform (python, javascript, etc.) |
| `dateCreated` | string | ISO 8601 creation timestamp |
| `crashFile` | string \| null | Crash file URL |
| `metadata` | unknown \| null | Event metadata |
| `metadata` | object \| null | Event metadata |

### `sentry issue explain <issue>`

Expand Down
4 changes: 2 additions & 2 deletions plugins/sentry-cli/skills/sentry-cli/references/replay.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ List recent Session Replays
| `releases` | array | Associated releases |
| `sdk` | object \| null | SDK metadata |
| `started_at` | string \| null | Replay start timestamp |
| `tags` | unknown | Replay tags |
| `tags` | object | Replay tags |
| `trace_ids` | array | Linked trace IDs |
| `urls` | array | Visited URLs |
| `user` | object \| null | User metadata |
Expand Down Expand Up @@ -118,7 +118,7 @@ View a Session Replay
| `releases` | array | Associated releases |
| `sdk` | object \| null | SDK metadata |
| `started_at` | string \| null | Replay start timestamp |
| `tags` | unknown | Replay tags |
| `tags` | object | Replay tags |
| `trace_ids` | array | Linked trace IDs |
| `urls` | array | Visited URLs |
| `user` | object \| null | User metadata |
Expand Down
2 changes: 1 addition & 1 deletion plugins/sentry-cli/skills/sentry-cli/references/team.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ List teams
| `id` | string | Team ID |
| `slug` | string | Team slug |
| `name` | string | Team name |
| `dateCreated` | string | Creation date (ISO 8601) |
| `dateCreated` | string \| null | Creation date (ISO 8601) |
| `isMember` | boolean | Whether you are a member |
| `teamRole` | string \| null | Your role in the team |
| `memberCount` | number | Number of members |
Expand Down
59 changes: 47 additions & 12 deletions src/lib/formatters/output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,11 +340,51 @@ export type SchemaFieldInfo = {
optional: boolean;
};

/** Leaf-type name mapping for {@link zodTypeToString} */
const ZOD_TYPE_MAP: Record<string, string> = {
ZodString: "string",
ZodNumber: "number",
ZodBoolean: "boolean",
ZodObject: "object",
ZodArray: "array",
ZodRecord: "object",
ZodNull: "null",
ZodUnknown: "unknown",
ZodAny: "any",
ZodEnum: "string",
ZodLiteral: "string",
};

/**
* Resolve a `ZodUnion` into a deduplicated `" | "`-joined type string.
*
* Used by auto-generated `@sentry/api/zod` schemas that represent nullable
* fields as `z.union([z.string(), z.null()])` instead of `z.string().nullable()`.
*/
function resolveZodUnion(options: ZodType[]): {
type: string;
optional: boolean;
} {
let optional = false;
const parts: string[] = [];
for (const opt of options) {
const resolved = zodTypeToString(opt);
if (resolved.optional) {
optional = true;
}
if (!parts.includes(resolved.type)) {
parts.push(resolved.type);
}
}
return { type: parts.join(" | "), optional };
}

/**
* Map a Zod type's internal `typeName` to a human-readable string.
*
* Unwraps wrapper types (Optional, Nullable, Default) and builds a
* composite type string (e.g. "string | null" for ZodNullable<ZodString>).
* Delegates ZodUnion handling to {@link resolveZodUnion}.
*/
function zodTypeToString(schema: ZodType): { type: string; optional: boolean } {
const def = (schema as { _def?: { typeName?: string; innerType?: ZodType } })
Expand All @@ -368,19 +408,14 @@ function zodTypeToString(schema: ZodType): { type: string; optional: boolean } {
if (def.typeName === "ZodDefault" && def.innerType) {
return zodTypeToString(def.innerType);
}
if (def.typeName === "ZodUnion") {
const options = (def as { options?: ZodType[] }).options;
if (options?.length) {
return resolveZodUnion(options);
}
}

const TYPE_MAP: Record<string, string> = {
ZodString: "string",
ZodNumber: "number",
ZodBoolean: "boolean",
ZodObject: "object",
ZodArray: "array",
ZodUnknown: "unknown",
ZodAny: "any",
ZodEnum: "string",
};

return { type: TYPE_MAP[def.typeName] ?? "unknown", optional: false };
return { type: ZOD_TYPE_MAP[def.typeName] ?? "unknown", optional: false };
}

/**
Expand Down
Loading
Loading