Skip to content

feat(cli): Complete --fields on org, project, release, dashboard, alert, and auth commands - #1783

Merged
BYK merged 7 commits into
mainfrom
feat/cli-fields-schemas
Nov 2, 2026
Merged

BYK merged 7 commits into
mainfrom
feat/cli-fields-schemas

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Follow-up to #1759. --fields only completes when a command's output config has a schema, so it still completed nothing on 115 commands. This adds output schemas to 29 of them, which leaves 86. It also fixes 12 commands whose jsonTransform silently ignored --fields (#1808), and adds a lint rule so that can't come back.

Schemas added:

  • org: list, view
  • project: list, view
  • release: list, view, create, archive, restore, finalize, set-commits, delete, deploys, deploy
  • dashboard: list, view, revisions, create, restore, widget add, widget edit, widget delete
  • alert issues and alert metrics: create, edit, delete
  • auth: list

Each schema describes what --json actually prints:

  • For list commands it describes the list item, which is what --fields filters.
  • It includes fields the command adds, such as orgSlug, targetProject, region/account, project dsn, dashboard url, and the --dry-run fields.
  • It leaves out what the command strips, such as detectedFrom, rendererPreference and graphicsCap.

The org, project, release and deploy schemas follow the SentryTeamSchema pattern. Each one curates the user-facing fields of the @sentry/api/valibot response schema and adds descriptions. alert issues|metrics edit spread the SDK's workflow and detector update responses (vUpdateOrganizationWorkflowResponse, vUpdateOrganizationDetectorResponse). All of them use looseObject, so other API fields still pass through. The dashboard commands reuse the existing DashboardListItemSchema, DashboardDetailSchema and DashboardRevisionSchema. Those three are runtime-validation schemas, and this PR doesn't change them.

None of the new schemas validate anything at runtime. OutputConfig.schema only feeds --fields completion, --help and the generated skill references. A test in complete.test.ts covers top-level and nested completion on release view/list, org view, project view and dashboard view.

release create --dry-run now has the same shape as a created release: projects is a list of { slug } objects instead of slug strings, and dateReleased replaces the finalize boolean. One schema (the release plus dryRun) covers both cases, so --fields projects.slug works either way.

--fields was silently ignored (#1808). A jsonTransform replaces the default JSON path, and that includes --fields filtering. These transforms never read their fields argument:

  • alert issues|metrics create|edit returned their input unchanged. I removed those transforms, so the default path does the filtering.
  • alert issues|metrics delete, alert issues view, auth list, dashboard widget delete, issue merge, issue resolve and project delete now filter their result with a new filterFieldsIfSet helper.

Guard: the new sentry-cli/json-transform-uses-fields lint rule (lint-rules/cli-oxlint-plugin.js) fails when an inline or same-module jsonTransform, or any top-level jsonTransform* function, never reads its second parameter. It flagged exactly the 12 commands above, and it passes on main's other transforms. packages/cli/AGENTS.md documents it.

Still not completing (86):

  • Structured output, but outside this batch:
    • agent-conversation view, attribute list, dsn list, explore, info, platform list
    • issue explain/plan/archive/resolve/unresolve/merge/link/unlink
    • log view, span view, trace view, trace logs
    • release propose-version
    • project create, project delete
    • integration delete, trial start
    • auth status/whoami
    • cli defaults/fix/import/setup/uninstall/upgrade/feedback
    • the upload, bundle and download commands (build, code-mappings, dart-symbol-map, debug-files, proguard, react-native, snapshots, sourcemap, replay download, wasm-split)
    • status show, docs list/query, help, schema, event send
  • Alert issues/metrics list/view: left out to avoid the metric-alert dataset work happening in parallel.
  • No static schema yet: api (the shape depends on the endpoint), auth token, cli completion and mcp (bare strings), and auth login/logout/refresh/switch (status messages).
  • Local commands: out of scope here.

The review proposes moving these schemas into @sentry/api, centralizing --fields completion, and letting api pick its schema from the endpoint. The plan for that is in the review reply. Whether it lands in this PR or in follow-ups is up to you.

via @BYK.

--

View Session in Sentry

…commands

Add output schemas to 20 org, project, release, and dashboard commands
so --fields completes their JSON field paths and --help and the skill
references list the fields. The schemas describe what --json prints,
including fields the commands add (orgSlug, dsn, url, dry-run fields),
and are documentation only: nothing validates output against them.

Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>
@vercel

vercel Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
sentry-local Skipped Skipped Nov 2, 2026 11:04pm UTC

Request Review

@github-actions github-actions Bot added the risk: low PR risk score: low label Nov 1, 2026

@BYK BYK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open question: release create's dry-run output has projects: string[], but a created release has an array of project objects. Should the schema describe both with a union, or should the dry-run output change shape?

Fix the dry-run output shape please.

Free-form or no meaningful JSON: api (raw passthrough), auth token, cli completion and mcp (bare strings), auth login/logout/refresh/switch (status messages).

Well we can infer fields for api endpoints we know about, right? Especially if the shapes are baked into @sentry/api as I requested. Same for others, we should be able to provide some field name suggestions to all command, maybe except cli completion. Ideal state is all commands have proper JSON outputs with known schemas so the --fields completion code is centralized and just uses whatever is defined for the command. For api we may need to be creative and allow a callback to change it based on the selected endpoint.

--fields does nothing today: alert issues/metrics create/edit/delete, dashboard widget delete and auth list have a jsonTransform that ignores the parsed fields, so --fields is silently dropped. Their transforms need fixing before a schema would be honest.

Well this sounds pretty bad. We should file an issue to make sure this never happens again, and fix the existing issue. Can you do that?

}

/** JSON fields of {@link DashboardViewData}, for `--fields`. */
const DashboardViewOutputSchema = object({

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all this should live in @sentry/api, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly, yes. A dashboard's own fields (id, title, dateCreated, environment, widgets[]…) are already in @sentry/api as vGetOrganizationDashboardResponse. But the generator doesn't emit OpenAPI descriptions, so the CLI writes its own copy with descriptions. period, fetchedAt, url and each widget's data are computed by the CLI and don't come from any endpoint, so they'd stay here.

Plan (also in the review reply):

  1. Upstream (getsentry/sentry-api-schema): emit v.description() from the OpenAPI field descriptions. Today valibot.gen has no DescriptionAction at all. Then fix the descriptions in the OpenAPI spec in getsentry/sentry wherever they're missing.
  2. Then this schema becomes looseObject({ ...vGetOrganizationDashboardResponse.entries, ...cliComputedEntries }). The same goes for the org/project/release/deploy/team curations in types/sentry.ts. The CLI keeps only the fields it adds.
  3. Dashboard revisions have no response schema in the SDK yet, so that needs the endpoint documented in Sentry's OpenAPI spec first.

I haven't changed this here. It's a follow-up unless you want it in this PR.

Resolve the widget/add.ts import conflict by keeping both
DashboardWidgetChangeOutputSchema (output schema) and DEFAULT_WIDGET_TYPE
(dataset-field completers from #1782).

Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>
sentry-junior Bot and others added 3 commits November 2, 2026 19:22
The dry-run preview listed projects as slug strings and a finalize flag,
while a created release has project objects and dateReleased. Emit
projects as { slug } objects and dateReleased instead, so one schema
(the release plus dryRun) describes both and --fields paths such as
projects.slug work in either mode.

Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>
A jsonTransform replaces the default JSON path, including --fields
filtering, so a transform that never read its fields argument made
--fields a silent no-op (#1808). Fix the ones that did:

- alert issues/metrics create and edit: drop the identity transform so
  the default path filters.
- alert issues/metrics delete, alert issues view, auth list, dashboard
  widget delete, issue merge, issue resolve, project delete: filter what
  the transform returns with the new filterFieldsIfSet helper.

Add output schemas for alert issues/metrics create, edit and delete
(edit documents the @sentry/api workflow or detector update response it
spreads), auth list, and dashboard widget delete, so --fields completes
there too.

Guard against new cases with the sentry-cli/json-transform-uses-fields
lint rule: an inline or same-module jsonTransform, or any top-level
jsonTransform* function, must read its second parameter.

Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>
Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>
@github-actions github-actions Bot added risk: medium PR risk score: medium and removed risk: low PR risk score: low labels Nov 2, 2026
@sentry-junior sentry-junior Bot changed the title feat(cli): Complete --fields on org, project, release, and dashboard commands feat(cli): Complete --fields on org, project, release, dashboard, alert, and auth commands Nov 2, 2026
@sentry-junior

sentry-junior Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @BYK. Done in 47f50f0 and 31da9a5, and merged with main:

  • Dry-run shape: release create --dry-run now outputs projects: [{ slug }] and dateReleased (in place of finalize), so one schema (release + dryRun) describes both cases. Tests cover the shape and --fields projects.slug.
  • --fields silently ignored: filed CLI --fields silently ignored by custom jsonTransforms #1808 and fixed all 12 commands. The alert create/edit identity transforms are gone. The alert issues/metrics delete, alert issues view, auth list, dashboard widget delete, issue merge/resolve and project delete transforms now apply fields. The alert create/edit/delete, auth list and widget delete commands also got schemas.
  • Guard: a new sentry-cli/json-transform-uses-fields lint rule fails on any jsonTransform that never reads its fields parameter. On the base branch it flagged exactly those 12.

Proposed plan for the broader direction (not implemented here; this PR or follow-ups, your call):

  1. Schemas live in @sentry/api. It already ships v<Operation>Response valibot schemas for its operations, but without field descriptions: valibot.gen has no description() actions. That's why the CLI re-curates org/project/release/deploy/team/dashboard in types/sentry.ts and types/dashboard.ts. The fix is upstream in getsentry/sentry-api-schema: emit v.description() from the OpenAPI descriptions, and fill gaps in Sentry's OpenAPI spec (for example, the SDK has no response schema for dashboard revisions). After that, each CLI schema becomes looseObject({ ...v<Op>Response.entries, ...cliAddedEntries }), and only fields the CLI adds (orgSlug, dsn, url, dryRun, dashboard widget data/period/fetchedAt) stay local. The alert edit schemas in this PR already work this way.
  2. api picks its schema from the endpoint. Add an optional OutputConfig.schemaFor(args, flags) beside the static schema. For api, it normalizes the endpoint and --method, matches them to an operation with the existing findEndpointsByPath() in lib/api-schema.ts (backed by @sentry/api/operations), and maps that operation's functionName to v<FunctionName>Response. It uses the item schema for list responses, since formatApiResponseJson already filters body per item. To keep per-keystroke completion cheap, generate:sdk would precompute an operation→field-paths table, so completion doesn't load all of valibot.gen. --help keeps describing the envelope, because it can't know the endpoint.
  3. Centralized completion. buildFieldsFlag already derives completion from outputConfig.schema, so schemaFor just becomes the second input to that one path. Add a coverage test (like completion-coverage) that fails for any command with JSON output and neither schema nor schemaFor, with an explicit exemption list (cli completion, bare-string outputs).
  4. Commands left (86, listed in the PR body). Many likely map straight to SDK responses once step 1 lands: issue *, project create, trace/span/log view, integration delete, release propose-version. Status-message commands (auth login/logout/refresh/switch) can get small local schemas. Local commands and the upload commands stay local.

Suggested split: step 1 upstream, then a CLI PR moving the existing curations onto SDK entries, then schemaFor + api + the coverage test, then the remaining commands.

Resolve import conflicts with #1805 (shared toolkit-core Markdown
builders) by keeping both the valibot schema imports and the new
markdown helper imports. In release create's dry-run output, use
inlineCode() from main on the now string-typed version.

Co-Authored-By: Junior (Claude) <noreply@anthropic.com>
…schemas

Take every API field of the alert create/edit/delete output schemas (id,
name, the dry-run request body) from the @sentry/api/valibot schemas of
the endpoint each command calls, instead of hand-writing them. Only the
fields the CLI adds (org, project, the status label, dryRun, deleted)
stay hand-written.
@BYK
BYK enabled auto-merge (squash) November 2, 2026 23:10
@BYK
BYK merged commit cf0e478 into main Nov 2, 2026
31 of 32 checks passed
@BYK
BYK deleted the feat/cli-fields-schemas branch November 2, 2026 23:18
| `name` | string | Alert rule name |
| `status` | string | Rule status (active, disabled) |
| `dryRun` | boolean | True when --dry-run made no change |
| `body` | object | Request body that would be sent (--dry-run) |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metric create dry-run body schema does not match JSON output

--fields completion and skill docs type dry-run body as the nested detector request payload, but JSON prints the flat CLI body (query, aggregate, triggers, …). Nested paths like body.dataSources complete then project empty; real flat keys are not offered.

Evidence
  • MetricAlertCreateOutputSchema types body as vCreateOrganizationProjectDetectorData.entries.body with description "Request body that would be sent (--dry-run)" (output-schemas.ts).
  • alert metrics create dry-run yields the flat CLI body (name, query, aggregate, dataset, timeWindow, triggers, projects, …), not buildDetectorBody() (create.ts; asserted in create.test.ts).
  • The transport only builds the nested detector payload inside createMetricAlertRule → buildDetectorBody (alerts.ts).
  • buildFieldsFlag completes with extractSchemaFieldPaths(schema), so nested detector paths under body are offered against the flat output.

Identified by Warden · code-review · 3VV-PEG

BYK added a commit that referenced this pull request Nov 2, 2026
Fixes #1808.

A command's `output.jsonTransform` replaces the default JSON path in
`renderCommandOutput`, and that path is also where `--fields` filtering
happens. Twelve transforms never read their `fields` argument, so
`--fields` printed the full object without any warning. For example,
`sentry project delete my-org/my-app --json --fields project` printed
`{"deleted":true,"org":"my-org","project":"my-app"}`.

- `alert issues|metrics create|edit` returned their input unchanged. I
removed those transforms so the default path filters.
- `alert issues|metrics delete`, `alert issues view`, `auth list`,
`dashboard widget delete`, `issue merge`, `issue resolve` and `project
delete` now pass their result through a new `filterFieldsIfSet` helper.

To stop new cases, a new `sentry-cli/json-transform-uses-fields` lint
rule fails when a `jsonTransform` never reads its second parameter. That
covers inline transforms, same-module ones, and any top-level
`jsonTransform*` function. On `main` it reports exactly these 12. The
rule has `RuleTester` cases in `scripts/cli-lint-rules.test.mjs` and is
documented in `packages/cli/AGENTS.md`.

This is the #1808 part of #1783, pulled out without the new `--fields`
completion schemas so it can land on its own. After this merges, #1783
will need its copy of these changes reconciled with `main`, keeping only
its schema additions.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:local%3Aweb%3A57fd20ee8bdc8bf5fbf5d430 -->

--

[View Session in
Sentry](https://sentry.sentry.io/explore/conversations/local%3Aweb%3A57fd20ee8bdc8bf5fbf5d430/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <126780+BYK@users.noreply.github.com>
BYK added a commit that referenced this pull request Nov 3, 2026
Fixes #1824 using option 2 from the issue: write the schema first and
derive the types from it.

Fields the CLI adds to a command's JSON output were declared twice: once
in `OutputConfig.schema`, which feeds `--fields` completion, `--help`
and the skill references, and once in the TypeScript type the command
yields. Nothing kept the two in sync. These fields are target context,
`dryRun`/`deleted` flags, and computed values like `url`, `trace`,
`period` and widget `data`.

Now each command writes them once, as a valibot `object` of output
fields. The schema spreads that object's `entries`, and the type comes
from it, so the compiler catches drift:

- `InferOutput<typeof Fields>`, when the output is all CLI fields.
- `WithOutputFields<ApiType, typeof Fields>` (new,
`src/types/output.ts`), when the fields sit on or override an
`@sentry/api` object. It uses a key-remapping mapped type, not `Omit`,
because `Omit` drops named keys from a `looseObject`'s index-signature
output.

API fields still come from the generated `@sentry/api/valibot` schemas,
as in #1783.

### What moved

- **alert issues/metrics create/edit/delete**: the output and
create-body types come from `alert/output-schemas.ts`. Delete commands
now yield their JSON shape directly (`{dryRun|deleted, org, [project],
id, name}`), so their `jsonTransform`s are gone.
- **release archive/restore/finalize/deploy/create/delete/list**: added
`ReleaseUpdateOutputFields`, plus per-command fields for the `--dry-run`
previews. Formatters take the derived type instead of `Record<string,
unknown>` plus a cast.
- **dashboard view/create/restore/widget add/edit/delete**: the widget
data result union is now a valibot `variant`, and the TS types are
inferred from it. Schema traversal and type display in
`lib/formatters/output.ts` treat `variant` like `union`. Widget delete
yields its JSON shape directly, and its transform is gone.
- **Org-scoped lists** (`buildOrgListCommand`, `project list`):
`orgListItemSchema` and `WithOrg<T>` add `orgSlug` to both schema and
type.
- **project view, org list, auth list, issue view, event view, feedback
view, replay view**: enrichment fields (`org`, `event`, `trace`,
`attachments`, `replayIds`, `activity`, `relatedIssues`, …) are declared
once in `*OutputFields`. Full event payloads stay opaque in the docs
with `opaque<SentryEvent>()`.
- **auth list**: `AccountSummarySchema` moved to `src/types/account.ts`.
`lib/db/accounts.ts` takes `AccountSummary` from it with a type-only
import, because the `__complete` fast path loads that module and must
not load valibot (`test/script/cli-startup.test.ts`).
- **`packages/cli/AGENTS.md`**: new "Declaring Command Output" section
on how a new command declares its output.

### Generated docs

The only change is one new `orgSlug` row in each of the `team`, `repo`,
`integration` and `monitor` skill references. Those lists already put
`orgSlug` on every item in their JSON output; the schema just didn't
document it.

### Not covered

- `buildCommand` doesn't tie `OutputConfig<T>` to the type `func`
yields, so the check depends on typing the formatter's parameter and the
`CommandOutput<T>` yields. Wiring `T` through `buildCommand` would be a
separate, wider change.
- Doc schemas that only describe API data (`SentryFeedbackSchema`,
`IssueEventSchema`, the replay list schema, `issue tags`) are unchanged.
Per the issue, API field descriptions wait on upstream `@sentry/api`
support.

### Checks

- `pnpm run typecheck` and `pnpm run lint` (CLI package) pass after
rebasing on `main`. Root `pnpm run lint` passed before the rebase.
- `pnpm run test:unit`: 13,058 passed, 7 failed. Six failures are
permission tests that also fail on the base commit in this sandbox
(completions, upgrade, resilient DB, code scanner, init file changes).
The seventh was the startup-import regression above, now fixed.
- After the rebase I reran the alert, dashboard, release, auth, org,
project, issue view, feedback, formatter and cli-startup tests (2,036
tests); all pass. Event and replay tests passed before the rebase.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:local%3Aweb%3A90128b345a91a7f5dc7c6a69 -->

--

[View Session in
Sentry](https://sentry.sentry.io/explore/conversations/local%3Aweb%3A90128b345a91a7f5dc7c6a69/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <126780+BYK@users.noreply.github.com>

This branch was previously deployed

1 inactive deployment
Preview — c8c76c6b Deployed Nov 2, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant