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 apps/cli-docs/src/components/Terminal.astro
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ if (background) {
<div class="terminal-body">
<div class="line">
<span class="prompt">$</span>
<span class="command">sentry auth login</span>
<span class="command">sentry auth</span>
</div>
<div class="line output">
<span class="icon blue">●</span>
Expand Down
6 changes: 3 additions & 3 deletions apps/cli-docs/src/content/docs/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Or run directly without installing:
The easiest way to authenticate is via OAuth device flow:

```bash
sentry auth login
sentry auth
```

You'll be given a URL and a code to enter. Once you authorize the application
Expand All @@ -120,7 +120,7 @@ Sentry CLI configuration directory (`~/.sentry/` by default, overridable with
Alternatively, you can use an API token directly:

```bash
sentry auth login --token YOUR_SENTRY_API_TOKEN
sentry auth --token YOUR_SENTRY_API_TOKEN
```

You can create API tokens in your [Sentry account settings](https://sentry.io/settings/account/api/auth-tokens/).
Expand Down Expand Up @@ -149,7 +149,7 @@ sentry auth logout
Using a self-hosted Sentry instance? Set `SENTRY_URL` to point at it:

```bash
SENTRY_URL=https://sentry.example.com sentry auth login
SENTRY_URL=https://sentry.example.com sentry auth
```

See the [Self-Hosted](../self-hosted/) guide for full setup details.
Expand Down
19 changes: 11 additions & 8 deletions apps/cli-docs/src/fragments/commands/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
### OAuth login (recommended)

```bash
sentry auth login
sentry auth
```

Bare `sentry auth` logs in when you're logged out and shows status when you're
already authenticated. `sentry auth login` always starts the login flow.

1. A URL and device code will be displayed
2. Open the URL in your browser
3. Enter the code when prompted
Expand All @@ -18,7 +21,7 @@ sentry auth login
### Token login

```bash
sentry auth login --token YOUR_SENTRY_API_TOKEN
sentry auth --token YOUR_SENTRY_API_TOKEN
```

### Read-only OAuth login
Expand All @@ -27,31 +30,31 @@ Request only read-only scopes — useful for tokens handed to AI agents or
CI jobs that should not mutate Sentry state:

```bash
sentry auth login --read-only
sentry auth --read-only
```

### Custom OAuth scopes

Request specific scopes (repeatable, comma-separated):

```bash
sentry auth login --scope project:read --scope org:read
sentry auth login --scope project:read,event:read
sentry auth --scope project:read --scope org:read
sentry auth --scope project:read,event:read
```

### Self-hosted Sentry

Use `--url` (recommended) or the `SENTRY_URL` environment variable:

```bash
sentry auth login --url https://sentry.example.com
SENTRY_URL=https://sentry.example.com sentry auth login
sentry auth --url https://sentry.example.com
SENTRY_URL=https://sentry.example.com sentry auth
```

For token-based auth with self-hosted:

```bash
sentry auth login --token YOUR_TOKEN --url https://sentry.example.com
sentry auth --token YOUR_TOKEN --url https://sentry.example.com
```

See [Self-Hosted Sentry](../self-hosted/) for details.
Expand Down
2 changes: 1 addition & 1 deletion apps/cli-docs/src/fragments/commands/init.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> **Experimental:** `sentry init` is experimental and may modify your source files. Always review changes before committing.

**Prerequisites:** You must be authenticated first. Run `sentry auth login` if you haven't already.
**Prerequisites:** You must be authenticated first. Run `sentry auth` if you haven't already.

## Examples

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ bunx sentry --help

```bash
# Authenticate with Sentry
sentry auth login
sentry auth

# List issues (auto-detects project from your codebase)
sentry issue list
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/plugins/sentry-cli/skills/sentry-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,8 @@ npm install -g sentry
### Authentication

```bash
sentry auth login
sentry auth login --token YOUR_SENTRY_API_TOKEN
sentry auth
sentry auth --token YOUR_SENTRY_API_TOKEN
sentry auth status
sentry auth logout
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,19 @@ Authenticate with Sentry
**Examples:**

```bash
sentry auth login
sentry auth

sentry auth login --token YOUR_SENTRY_API_TOKEN
sentry auth --token YOUR_SENTRY_API_TOKEN

sentry auth login --read-only
sentry auth --read-only

sentry auth login --scope project:read --scope org:read
sentry auth login --scope project:read,event:read
sentry auth --scope project:read --scope org:read
sentry auth --scope project:read,event:read

sentry auth login --url https://sentry.example.com
SENTRY_URL=https://sentry.example.com sentry auth login
sentry auth --url https://sentry.example.com
SENTRY_URL=https://sentry.example.com sentry auth

sentry auth login --token YOUR_TOKEN --url https://sentry.example.com
sentry auth --token YOUR_TOKEN --url https://sentry.example.com
```

### `sentry auth logout`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,10 @@ Mark an issue as resolved
**Flags:**
- `-i, --in <value> - Resolve in a release, next release, or commit ('<version>' | '@next' | '@commit' | '@commit:<repo>@<sha>')`

### `sentry issue unresolve <issue>`

Reopen a resolved issue

**Examples:**

```bash
Expand Down Expand Up @@ -265,10 +269,6 @@ sentry issue unresolve CLI-G5
sentry issue reopen CLI-G5 # alias
```

### `sentry issue unresolve <issue>`

Reopen a resolved issue

### `sentry issue archive <issue>`

Archive (ignore) an issue
Expand Down
20 changes: 15 additions & 5 deletions packages/cli/script/generate-skill-markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,20 @@ export function extractCommandPathFromHeading(
export function matchExampleToCommand(
code: string,
commandPaths: readonly string[],
groupFallback: string
groupFallback: string,
defaultCommandPath?: string
): string | undefined {
return (
commandPaths.find((path) => code.includes(path)) ??
(code.includes(groupFallback) ? groupFallback : undefined)
);
// Prefer the longest path so `sentry auth login` wins over bare `sentry auth`
// when both would otherwise match via includes().
const byLengthDesc = [...commandPaths].sort((a, b) => b.length - a.length);
const matched = byLengthDesc.find((path) => code.includes(path));
if (matched) {
return matched;
}
if (!code.includes(groupFallback)) {
return;
}
// Bare group examples (`sentry auth`) belong on the default subcommand when
// one exists (login), not on a synthetic group-only path.
return defaultCommandPath ?? groupFallback;
}
70 changes: 61 additions & 9 deletions packages/cli/script/generate-skill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,34 @@ import {
isRouteMap,
} from "../src/lib/introspect.js";

/**
* Resolve the default subcommand name for a route map, if configured.
*
* Hidden dispatcher routes (e.g. auth's `default`) map bare group examples
* onto the user-facing command they primarily document (login).
*/
function findDefaultCommandName(routeMap: RouteMap): string | undefined {
if (!routeMap.getDefaultCommand) {
return;
}
const defaultCmd = routeMap.getDefaultCommand();
if (!defaultCmd) {
return;
}
for (const sub of routeMap.getAllEntries()) {
if (sub.target === defaultCmd) {
const name = sub.name.original;
// Auth uses a hidden "default" dispatcher; bare `sentry auth` examples
// document the login path.
if (name === "default") {
return "login";
}
return name;
}
}
return;
}

const SKILL_DIR = "plugins/sentry-cli/skills/sentry-cli";
const OUTPUT_PATH = `${SKILL_DIR}/SKILL.md`;
const REFERENCES_DIR = `${SKILL_DIR}/references`;
Expand Down Expand Up @@ -290,10 +318,10 @@ npm install -g sentry

\`\`\`bash
# OAuth login (recommended)
sentry auth login
sentry auth

# Or use an API token
sentry auth login --token YOUR_SENTRY_API_TOKEN
sentry auth --token YOUR_SENTRY_API_TOKEN

# Check auth status
sentry auth status
Expand Down Expand Up @@ -341,10 +369,14 @@ function collectCommandPaths(
*/
function associateCodeBlocks(
tokens: Token[],
commandPaths: string[],
commandGroup: string,
examples: Map<string, string[]>
context: {
commandPaths: string[];
commandGroup: string;
examples: Map<string, string[]>;
defaultCommandPath?: string;
}
): void {
const { commandPaths, commandGroup, examples, defaultCommandPath } = context;
const groupFallback = `sentry ${commandGroup}`;
let currentCmd: string | null = null;

Expand All @@ -359,7 +391,12 @@ function associateCodeBlocks(
if (currentCmd && examples.has(currentCmd)) {
appendExample(examples, currentCmd, code);
} else {
const target = matchExampleToCommand(code, commandPaths, groupFallback);
const target = matchExampleToCommand(
code,
commandPaths,
groupFallback,
defaultCommandPath
);
if (target) {
appendExample(examples, target, code);
}
Expand All @@ -377,7 +414,8 @@ function associateCodeBlocks(
* by matching code blocks to commands via heading context or content analysis.
*/
async function loadCommandExamples(
commandGroup: string
commandGroup: string,
defaultCommandName?: string
): Promise<Map<string, string[]>> {
const docContent = await loadDoc(`commands/${commandGroup}.md`);
if (!docContent) {
Expand All @@ -387,7 +425,15 @@ async function loadCommandExamples(
const tokens = marked.lexer(docContent);
const examples = new Map<string, string[]>();
const commandPaths = collectCommandPaths(tokens, examples);
associateCodeBlocks(tokens, commandPaths, commandGroup, examples);
const defaultCommandPath = defaultCommandName
? `sentry ${commandGroup} ${defaultCommandName}`
: undefined;
associateCodeBlocks(tokens, {
commandPaths,
commandGroup,
examples,
defaultCommandPath,
});
return examples;
}

Expand Down Expand Up @@ -435,7 +481,13 @@ async function extractRoutes(routeMap: RouteMap): Promise<RouteInfo[]> {
}
const routeName = entry.name.original;
const target = entry.target;
const docExamples = await loadCommandExamples(routeName);
const defaultCommandName = isRouteMap(target)
? findDefaultCommandName(target)
: undefined;
const docExamples = await loadCommandExamples(
routeName,
defaultCommandName
);
if (isRouteMap(target)) {
result.push({
name: routeName,
Expand Down
Loading
Loading