Skip to content
Merged
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
2 changes: 1 addition & 1 deletion packages/cli/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ Reference: `project/create.ts`, `release/set-commits.ts` (`--path`), `auth/login

All non-trivial human output must use the markdown rendering pipeline:

- Build markdown strings with helpers: `mdKvTable()`, `colorTag()`, `escapeMarkdownCell()`, `renderMarkdown()`
- Build markdown strings with helpers: `mdKvTable()`, `colorTag()`, `renderMarkdown()`, and `text()`, `tableCell()`, `inlineCode()` from `@sentry/toolkit-core/markdown`
- **NEVER** use raw `muted()` / chalk in output strings — use `colorTag("muted", text)` inside markdown
- Tree-structured output (box-drawing characters) that can't go through `renderMarkdown()` should use the `plainSafeMuted` pattern: `isPlainOutput() ? text : muted(text)`
- `isPlainOutput()` precedence: `SENTRY_PLAIN_OUTPUT` > `NO_COLOR` > `FORCE_COLOR` (TTY only) > `!isTTY`
Expand Down
8 changes: 3 additions & 5 deletions packages/cli/src/commands/alert/issues/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* - project-search → find project by slug across all orgs
*/

import { tableCell } from "@sentry/toolkit-core/markdown";
import type { SentryContext } from "../../../context.js";
import { buildProjectAliasMap } from "../../../lib/alias.js";
import type { IssueAlertRule } from "../../../lib/api/alerts.js";
Expand Down Expand Up @@ -39,10 +40,7 @@ import {
ValidationError,
withAuthGuard,
} from "../../../lib/errors.js";
import {
colorTag,
escapeMarkdownCell,
} from "../../../lib/formatters/markdown.js";
import { colorTag } from "../../../lib/formatters/markdown.js";
import { CommandOutput } from "../../../lib/formatters/output.js";
import { type Column, writeTable } from "../../../lib/formatters/table.js";
import {
Expand Down Expand Up @@ -454,7 +452,7 @@ function formatIssueAlertListHuman(result: IssueAlertListResult): string {

const tableRows: Row[] = rows.map(({ rule: r, target }) => ({
id: r.id,
name: escapeMarkdownCell(r.name ?? "(untitled)"),
name: tableCell(r.name ?? "(untitled)"),
...(isMultiProject && {
project: `${target.org}/${target.project}`,
}),
Expand Down
8 changes: 3 additions & 5 deletions packages/cli/src/commands/alert/metrics/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* - project-search → find project across orgs, use its org
*/

import { tableCell } from "@sentry/toolkit-core/markdown";
import type { SentryContext } from "../../../context.js";
import type { MetricAlertRule } from "../../../lib/api/alerts.js";
import { paginate } from "../../../lib/api/infrastructure.js";
Expand All @@ -36,10 +37,7 @@ import {
ValidationError,
withAuthGuard,
} from "../../../lib/errors.js";
import {
colorTag,
escapeMarkdownCell,
} from "../../../lib/formatters/markdown.js";
import { colorTag } from "../../../lib/formatters/markdown.js";
import { CommandOutput } from "../../../lib/formatters/output.js";
import { type Column, writeTable } from "../../../lib/formatters/table.js";
import {
Expand Down Expand Up @@ -465,7 +463,7 @@ function formatMetricAlertListHuman(result: MetricAlertListResult): string {

const tableRows: Row[] = rows.map(({ rule: r, orgSlug }) => ({
id: r.id,
name: escapeMarkdownCell(r.name ?? "(untitled)"),
name: tableCell(r.name ?? "(untitled)"),
...(isMultiOrg && { org: orgSlug }),
aggregate: r.aggregate,
dataset: r.dataset,
Expand Down
5 changes: 3 additions & 2 deletions packages/cli/src/commands/dashboard/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* and optional client-side glob filtering by title.
*/

import { tableCell } from "@sentry/toolkit-core/markdown";
import picomatch from "picomatch";
import type { SentryContext } from "../../context.js";
import { MAX_PAGINATION_PAGES } from "../../lib/api/infrastructure.js";
Expand All @@ -20,7 +21,7 @@ import {
resolveCursor,
} from "../../lib/db/pagination.js";
import { filterFields } from "../../lib/formatters/json.js";
import { colorTag, escapeMarkdownCell } from "../../lib/formatters/markdown.js";
import { colorTag } from "../../lib/formatters/markdown.js";
import { CommandOutput } from "../../lib/formatters/output.js";
import { type Column, writeTable } from "../../lib/formatters/table.js";
import { fuzzyMatch } from "../../lib/fuzzy.js";
Expand Down Expand Up @@ -153,7 +154,7 @@ function formatDashboardListHuman(result: DashboardListResult): string {
const url = buildDashboardUrl(result.orgSlug, d.id);
return {
id: d.id,
title: `${escapeMarkdownCell(d.title ?? "(untitled)")}\n${colorTag("muted", url)}`,
title: `${tableCell(d.title ?? "(untitled)")}\n${colorTag("muted", url)}`,
widgets: String(d.widgetDisplay?.length ?? 0),
};
});
Expand Down
7 changes: 4 additions & 3 deletions packages/cli/src/commands/dashboard/restore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
* Restore a dashboard to a previous revision.
*/

import { tableCell } from "@sentry/toolkit-core/markdown";
import type { SentryContext } from "../../context.js";
import { restoreDashboardRevision } from "../../lib/api-client.js";
import { buildCommand } from "../../lib/command.js";
import { textParser } from "../../lib/completion-target.js";
import { ValidationError } from "../../lib/errors.js";
import { colorTag, escapeMarkdownCell } from "../../lib/formatters/markdown.js";
import { colorTag } from "../../lib/formatters/markdown.js";
import { CommandOutput } from "../../lib/formatters/output.js";
import { formatRelativeTime } from "../../lib/formatters/time-utils.js";
import { withProgress } from "../../lib/polling.js";
Expand Down Expand Up @@ -37,11 +38,11 @@ function formatRestoreHuman(result: RestoreResult): string {
const created = formatRelativeTime(d.dateCreated);

return (
`Restored dashboard **${escapeMarkdownCell(d.title)}** to revision ${result.revisionId}.\n\n` +
`Restored dashboard **${tableCell(d.title)}** to revision ${result.revisionId}.\n\n` +
"| Field | Value |\n" +
"|-------|-------|\n" +
`| ID | ${d.id} |\n` +
`| Title | ${escapeMarkdownCell(d.title)} |\n` +
`| Title | ${tableCell(d.title)} |\n` +
`| Widgets | ${widgetCount} |\n` +
`| Created | ${created} |\n` +
`| URL | ${colorTag("muted", url)} |`
Expand Down
9 changes: 5 additions & 4 deletions packages/cli/src/commands/dashboard/revisions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* List revision history for a Sentry dashboard with cursor-based pagination.
*/

import { tableCell } from "@sentry/toolkit-core/markdown";
import type { SentryContext } from "../../context.js";
import { paginate } from "../../lib/api/infrastructure.js";
import { listDashboardRevisionsPaginated } from "../../lib/api-client.js";
Expand All @@ -15,7 +16,7 @@ import {
resolveCursor,
} from "../../lib/db/pagination.js";
import { filterFields } from "../../lib/formatters/json.js";
import { colorTag, escapeMarkdownCell } from "../../lib/formatters/markdown.js";
import { colorTag } from "../../lib/formatters/markdown.js";
import { CommandOutput } from "../../lib/formatters/output.js";
import { type Column, writeTable } from "../../lib/formatters/table.js";
import { formatRelativeTime } from "../../lib/formatters/time-utils.js";
Expand Down Expand Up @@ -63,11 +64,11 @@ function formatRevisionsHuman(result: RevisionsResult): string {

const rows: RevisionRow[] = result.revisions.map((r) => ({
id: r.id,
title: escapeMarkdownCell(r.title),
author: escapeMarkdownCell(
title: tableCell(r.title),
author: tableCell(
r.createdBy?.name ?? r.createdBy?.email ?? r.createdBy?.id ?? "—",
),
created: `${escapeMarkdownCell(formatRelativeTime(r.dateCreated))}\n${colorTag("muted", r.dateCreated)}`,
created: `${tableCell(formatRelativeTime(r.dateCreated))}\n${colorTag("muted", r.dateCreated)}`,
}));

const columns: Column<RevisionRow>[] = [
Expand Down
11 changes: 4 additions & 7 deletions packages/cli/src/commands/debug-files/print-sources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* the bundled `symbolic` WASM module (see `src/lib/dif/`).
*/

import { inlineCode } from "@sentry/toolkit-core/markdown";
import { existsSync } from "node:fs";
import type { SentryContext } from "../../context.js";
import { buildCommand } from "../../lib/command.js";
Expand All @@ -21,11 +22,7 @@ import {
} from "../../lib/dif/index.js";
import { parseFilePath } from "../../lib/completion-target.js";
import { ValidationError } from "../../lib/errors.js";
import {
colorTag,
renderMarkdown,
safeCodeSpan,
} from "../../lib/formatters/markdown.js";
import { colorTag, renderMarkdown } from "../../lib/formatters/markdown.js";
import { CommandOutput } from "../../lib/formatters/output.js";
import { logger } from "../../lib/logger.js";
import { readDebugFile } from "./read-file.js";
Expand Down Expand Up @@ -82,15 +79,15 @@ function formatPrintSources(data: PrintSourcesResult): string {
}

const sections = data.objects.map((object) => {
const header = `${object.fileFormat} ${safeCodeSpan(object.debugId)}`;
const header = `${object.fileFormat} ${inlineCode(object.debugId)}`;
if (object.enumerationError !== null) {
return `${header} — ${colorTag("muted", `could not read sources: ${object.enumerationError}`)}`;
}
if (object.files.length === 0) {
return `${header} — ${colorTag("muted", "no referenced sources")}`;
}
const lines = object.files.map(
(file) => `- ${safeCodeSpan(file.path)} — ${describeSource(file)}`,
(file) => `- ${inlineCode(file.path)} — ${describeSource(file)}`,
);
const count = object.files.length;
return `${header} — ${count} source file${count === 1 ? "" : "s"}:\n\n${lines.join("\n")}`;
Expand Down
9 changes: 5 additions & 4 deletions packages/cli/src/commands/event/shared-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
* identities and pagination hints.
*/

import { tableCell } from "@sentry/toolkit-core/markdown";
import { validateLimit } from "../../lib/arg-parsing.js";
import { textParser } from "../../lib/completion-target.js";
import { filterFields } from "../../lib/formatters/json.js";
import { colorTag, escapeMarkdownCell } from "../../lib/formatters/markdown.js";
import { colorTag } from "../../lib/formatters/markdown.js";
import { type Column, formatTable } from "../../lib/formatters/table.js";
import { formatRelativeTime } from "../../lib/formatters/time-utils.js";
import {
Expand Down Expand Up @@ -82,7 +83,7 @@ function getUserLabel(event: IssueEvent): string {
event.user.username ??
event.user.ip_address ??
event.user.id;
return label ? escapeMarkdownCell(label) : colorTag("muted", "—");
return label ? tableCell(label) : colorTag("muted", "—");
}

/**
Expand All @@ -105,12 +106,12 @@ export const EVENT_COLUMNS: Column<IssueEvent>[] = [
},
{
header: "TITLE",
value: (e) => escapeMarkdownCell(e.title),
value: (e) => tableCell(e.title),
truncate: true,
},
{
header: "PLATFORM",
value: (e) => escapeMarkdownCell(e.platform ?? "—"),
value: (e) => tableCell(e.platform ?? "—"),
},
{
header: "USER",
Expand Down
5 changes: 3 additions & 2 deletions packages/cli/src/commands/integration/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
* flags and enforces the non-interactive guard before `func()` runs.
*/

import { inlineCode } from "@sentry/toolkit-core/markdown";
import type { SentryContext } from "../../context.js";
import { deleteIntegration, getIntegration } from "../../lib/api-client.js";
import { ORG_TARGET } from "../../lib/completion-target.js";
import { ApiError, ValidationError } from "../../lib/errors.js";
import { renderMarkdown, safeCodeSpan } from "../../lib/formatters/markdown.js";
import { renderMarkdown } from "../../lib/formatters/markdown.js";
import { CommandOutput } from "../../lib/formatters/output.js";
import { ID_KINDS, parseIdArguments } from "../../lib/id-arguments.js";
import { runInteractiveLogin } from "../../lib/interactive-login.js";
Expand Down Expand Up @@ -61,7 +62,7 @@ type DeleteFlags = {
};

function formatIntegrationDeleted(result: DeleteResult): string {
const label = `${result.provider} integration ${safeCodeSpan(result.name)} (id ${result.id})`;
const label = `${result.provider} integration ${inlineCode(result.name)} (id ${result.id})`;
if (result.dryRun) {
return renderMarkdown(
`Would remove ${label} from **${result.org}**. (dry run)`,
Expand Down
8 changes: 4 additions & 4 deletions packages/cli/src/commands/integration/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
* and cursor pagination.
*/

import { tableCell } from "@sentry/toolkit-core/markdown";
import {
listIntegrations,
listIntegrationsPaginated,
} from "../../lib/api-client.js";
import { escapeMarkdownCell } from "../../lib/formatters/markdown.js";
import { type Column, formatTable } from "../../lib/formatters/table.js";
import {
buildOrgListCommand,
Expand All @@ -31,10 +31,10 @@ type IntegrationWithOrg = SentryIntegration & { orgSlug?: string };
const INTEGRATION_COLUMNS: Column<IntegrationWithOrg>[] = [
{ header: "ORG", value: (i) => i.orgSlug || "" },
{ header: "ID", value: (i) => i.id },
{ header: "PROVIDER", value: (i) => escapeMarkdownCell(i.provider.name) },
{ header: "NAME", value: (i) => escapeMarkdownCell(i.name) },
{ header: "PROVIDER", value: (i) => tableCell(i.provider.name) },
{ header: "NAME", value: (i) => tableCell(i.name) },
{ header: "STATUS", value: (i) => i.organizationIntegrationStatus },
{ header: "DOMAIN", value: (i) => escapeMarkdownCell(i.domainName || "") },
{ header: "DOMAIN", value: (i) => tableCell(i.domainName || "") },
];

/** Shared config that plugs into the org-list framework. */
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/commands/monitor/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
* - Cross-org project search (e.g., sentry)
*/

import { tableCell } from "@sentry/toolkit-core/markdown";
import { listMonitors, listMonitorsPaginated } from "../../lib/api-client.js";
import { escapeMarkdownCell } from "../../lib/formatters/markdown.js";
import { type Column, formatTable } from "../../lib/formatters/table.js";
import {
buildOrgListCommand,
Expand Down Expand Up @@ -53,9 +53,9 @@ export function formatSchedule(monitor: MonitorWithOrg): string {
const MONITOR_COLUMNS: Column<MonitorWithOrg>[] = [
{ header: "ID", value: (m) => m.id },
{ header: "SLUG", value: (m) => m.slug },
{ header: "NAME", value: (m) => escapeMarkdownCell(m.name) },
{ header: "NAME", value: (m) => tableCell(m.name) },
{ header: "STATUS", value: (m) => m.status },
{ header: "SCHEDULE", value: (m) => escapeMarkdownCell(formatSchedule(m)) },
{ header: "SCHEDULE", value: (m) => tableCell(formatSchedule(m)) },
];

/** Shared config that plugs into the org-list framework. */
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/org/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* when several are logged in.
*/

import { tableCell } from "@sentry/toolkit-core/markdown";
import type { SentryContext } from "../../context.js";
import { resolveAccountSelector } from "../../lib/account-selection.js";
import { listOrganizationsUncached } from "../../lib/api-client.js";
Expand Down Expand Up @@ -34,7 +35,6 @@ import { getAllOrgRegions } from "../../lib/db/regions.js";
import { ApiError, AuthError } from "../../lib/errors.js";
import { formatAccountLabel } from "../../lib/formatters/accounts.js";
import { getNestedValue } from "../../lib/formatters/json.js";
import { escapeMarkdownCell } from "../../lib/formatters/markdown.js";
import { CommandOutput } from "../../lib/formatters/output.js";
import { type Column, writeTable } from "../../lib/formatters/table.js";
import { filterByName } from "../../lib/list-filter.js";
Expand Down Expand Up @@ -185,7 +185,7 @@ function formatOrgListHuman(entries: OrgListEntry[]): string {
)
.map(([header, path]) => ({
header,
value: (e) => escapeMarkdownCell(cellOf(e, path) ?? ""),
value: (e) => tableCell(cellOf(e, path) ?? ""),
}));

const parts: string[] = [];
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/project/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* - Cross-org project search (e.g., sentry)
*/

import { tableCell } from "@sentry/toolkit-core/markdown";
import type { SentryContext } from "../../context.js";
import {
getProject,
Expand All @@ -35,7 +36,6 @@ import {
resolveCursor,
} from "../../lib/db/pagination.js";
import { ResolutionError, withAuthGuard } from "../../lib/errors.js";
import { escapeMarkdownCell } from "../../lib/formatters/markdown.js";
import {
CommandOutput,
type OutputConfig,
Expand Down Expand Up @@ -310,7 +310,7 @@ async function resolveOrgsForAutoDetect(cwd: string): Promise<OrgResolution> {
const PROJECT_COLUMNS: Column<ProjectWithOrg>[] = [
{ header: "ORG", value: (p) => p.orgSlug || "" },
{ header: "PROJECT", value: (p) => p.slug || "" },
{ header: "NAME", value: (p) => escapeMarkdownCell(p.name) },
{ header: "NAME", value: (p) => tableCell(p.name) },
{ header: "PLATFORM", value: (p) => p.platform || "" },
];

Expand Down
9 changes: 4 additions & 5 deletions packages/cli/src/commands/release/archive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@
* `sentry release restore`.
*/

import { inlineCode, text } from "@sentry/toolkit-core/markdown";
import type { SentryContext } from "../../context.js";
import { updateRelease } from "../../lib/api-client.js";
import { buildCommand } from "../../lib/command.js";
import {
colorTag,
escapeMarkdownInline,
mdKvTable,
renderMarkdown,
safeCodeSpan,
} from "../../lib/formatters/markdown.js";
import { CommandOutput } from "../../lib/formatters/output.js";
import { DRY_RUN_ALIASES, DRY_RUN_FLAG } from "../../lib/mutate-command.js";
Expand All @@ -31,15 +30,15 @@ const USAGE_HINT = "sentry release archive [<org>/]<version>";
function formatReleaseArchived(data: Record<string, unknown>): string {
if (data.dryRun) {
return renderMarkdown(
`Would archive release ${safeCodeSpan(String(data.version))} (dry run)`,
`Would archive release ${inlineCode(String(data.version))} (dry run)`,
);
}
const release = data as unknown as SentryRelease;
const lines: string[] = [];
lines.push(`## Release Archived: ${escapeMarkdownInline(release.version)}`);
lines.push(`## Release Archived: ${text(release.version)}`);
lines.push("");
const kvRows: [string, string][] = [];
kvRows.push(["Version", safeCodeSpan(release.version)]);
kvRows.push(["Version", inlineCode(release.version)]);
kvRows.push(["Status", colorTag("muted", "Archived")]);
lines.push(mdKvTable(kvRows));
return renderMarkdown(lines.join("\n"));
Expand Down
Loading
Loading