Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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