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
4 changes: 4 additions & 0 deletions apps/cli-docs/src/content/docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ description: Advanced features of the Sentry CLI

The Sentry CLI includes several features designed to streamline your workflow, especially in complex project setups.

## Interactive Selection

When the CLI asks you to choose an organization or project, type to filter the loaded options by name or slug. The filter appears as `/text` below the list. Matching ignores case and tolerates small typos. Use arrow keys to navigate, Enter to select, and Backspace to edit. Esc clears the filter before cancelling; Ctrl+C cancels immediately. The same controls work in the organization, team, and monorepo app selectors in `sentry init`.

## DSN Auto-Detection

The CLI automatically detects your Sentry project from your codebase, eliminating the need to specify the target for every command. DSN detection is one part of the [resolution priority chain](./configuration/#resolution-priority) — it runs after checking for explicit arguments, environment variables, and `.sentryclirc` config files.
Expand Down
2 changes: 2 additions & 0 deletions apps/cli-docs/src/fragments/commands/init.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

**Authentication:** Interactive runs start the OAuth login flow automatically when credentials are missing or the stored session has expired. Non-interactive and CI runs must authenticate before running `sentry init`.

In organization, team, and monorepo app selectors, type to filter the loaded choices by name or slug. The filter appears as `/text` below the list. Matching ignores case and tolerates small typos. Use arrow keys to navigate, Enter to select, and Backspace to edit. Esc clears an active filter; with no filter, Esc cancels. Ctrl+C always cancels.

## Examples

```bash
Expand Down
74 changes: 74 additions & 0 deletions packages/cli/script/preview-selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* Preview the real entity pickers with local fixtures, without authentication
* or project changes. Run `pnpm tsx script/preview-selectors.ts`; add `--compact`
* for the Org/Project picker used outside the init wizard.
*/
import { createInkUI, selectWithFilter } from "../src/lib/init/ui/ink-ui.js";
import { isCancelled, type SelectOptions } from "../src/lib/init/ui/types.js";

const organization: SelectOptions<string> = {
message: "Which organization should the project be created in?",
filterable: true,
options: [
{ value: "acme", label: "Acme", hint: "acme" },
{ value: "frontend-labs", label: "Frontend Labs", hint: "frontend-labs" },
{ value: "sandbox", label: "Sandbox", hint: "sandbox" },
],
};
const team: SelectOptions<string> = {
message: "Which team should own this project?",
filterable: true,
options: [
{ value: "backend", label: "backend", hint: "Backend Engineering" },
{ value: "frontend", label: "frontend", hint: "Web Engineering" },
{ value: "mobile", label: "mobile", hint: "Mobile Engineering" },
{ value: "platform", label: "platform", hint: "Developer Platform" },
],
};
const project: SelectOptions<string> = {
message: "Select a project:",
filterable: true,
options: [
"api-gateway",
"backend-workers",
"frontend-admin",
"frontend-web",
"mobile-ios",
"payments-api",
"storefront-api",
].map((value) => ({ value, label: value })),
};

async function preview(): Promise<void> {
if (!(process.stdin.isTTY && process.stdout.isTTY)) {
throw new Error("Run this preview in an interactive terminal.");
}
const chosen: string[] = [];
if (process.argv.includes("--compact")) {
for (const prompt of [organization, project]) {
const value = await selectWithFilter(prompt);
if (value === null) {
return;
}
chosen.push(value);
}
} else {
await using ui = await createInkUI();
ui.setIntroMode(true);
for (const prompt of [
organization,
team,
{ ...project, message: "Which app would you like to configure?" },
]) {
const value = await ui.select(prompt);
if (isCancelled(value)) {
return;
}
chosen.push(value);
}
ui.outro("Selector preview complete");
}
process.stdout.write(`Selected: ${chosen.join(" / ")}\n`);
}

await preview();
1 change: 1 addition & 0 deletions packages/cli/src/lib/init/interactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ async function handleSelect(

const selected = await ui.select<string>({
message: payload.prompt,
filterable: apps.length > 0,
options: items.map((item) => {
const app = apps.find((a) => a.name === item);
return {
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/lib/init/preflight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,7 @@ async function resolveImplicitTeam(

const selected = await ui.select<string>({
message: "Which team should own this project?",
filterable: true,
options: candidateTeams.map((team) => ({
value: team.slug,
label: team.slug,
Expand Down Expand Up @@ -573,6 +574,7 @@ async function resolveOrgSlug(

const selected = await ui.select<string>({
message: "Which organization should the project be created in?",
filterable: true,
options: orgs.map((org) => ({
value: org.slug,
label: org.name,
Expand Down
Loading
Loading