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
52 changes: 26 additions & 26 deletions plugins/sentry-cli/skills/sentry-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ View authentication status

**Flags:**
- `--show-token - Show the stored token (masked by default)`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand All @@ -114,7 +114,7 @@ Print the stored authentication token
Show the currently authenticated user

**Flags:**
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand All @@ -128,7 +128,7 @@ List organizations

**Flags:**
- `-n, --limit <value> - Maximum number of organizations to list - (default: "30")`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand All @@ -146,7 +146,7 @@ View details of an organization

**Flags:**
- `-w, --web - Open in browser`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand Down Expand Up @@ -182,7 +182,7 @@ List projects
- `-n, --limit <value> - Maximum number of projects to list - (default: "30")`
- `-c, --cursor <value> - Pagination cursor (use "last" to continue from previous page)`
- `-p, --platform <value> - Filter by platform (e.g., javascript, python)`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand All @@ -205,7 +205,7 @@ View details of a project

**Flags:**
- `-w, --web - Open in browser`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand Down Expand Up @@ -240,7 +240,7 @@ List issues in a project
- `-s, --sort <value> - Sort by: date, new, freq, user - (default: "date")`
- `-t, --period <value> - Time period for issue activity (e.g. 24h, 14d, 90d) - (default: "90d")`
- `-c, --cursor <value> - Pagination cursor for <org>/ or multi-target modes (use "last" to continue)`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--compact - Single-line rows for compact output (auto-detects if omitted)`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`
Expand Down Expand Up @@ -287,7 +287,7 @@ Analyze an issue's root cause using Seer AI

**Flags:**
- `--force - Force new analysis even if one exists`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand Down Expand Up @@ -316,7 +316,7 @@ Generate a solution plan using Seer AI
**Flags:**
- `--cause <value> - Root cause ID to plan (required if multiple causes exist)`
- `--force - Force new plan even if one exists`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand Down Expand Up @@ -345,7 +345,7 @@ View details of a specific issue
**Flags:**
- `-w, --web - Open in browser`
- `--spans <value> - Span tree depth limit (number, "all" for unlimited, "no" to disable) - (default: "3")`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand Down Expand Up @@ -374,7 +374,7 @@ View details of a specific event
**Flags:**
- `-w, --web - Open in browser`
- `--spans <value> - Span tree depth limit (number, "all" for unlimited, "no" to disable) - (default: "3")`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand Down Expand Up @@ -508,7 +508,7 @@ List repositories
**Flags:**
- `-n, --limit <value> - Maximum number of repositories to list - (default: "30")`
- `-c, --cursor <value> - Pagination cursor (use "last" to continue from previous page)`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand All @@ -523,7 +523,7 @@ List teams
**Flags:**
- `-n, --limit <value> - Maximum number of teams to list - (default: "30")`
- `-c, --cursor <value> - Pagination cursor (use "last" to continue from previous page)`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand Down Expand Up @@ -555,7 +555,7 @@ List logs from a project
- `-q, --query <value> - Filter query (Sentry search syntax)`
- `-f, --follow <value> - Stream logs (optionally specify poll interval in seconds)`
- `--trace <value> - Filter logs by trace ID (32-character hex string)`
- `--fresh - Bypass cache and fetch fresh data`
- `--fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand Down Expand Up @@ -602,7 +602,7 @@ View details of one or more log entries

**Flags:**
- `-w, --web - Open in browser`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand Down Expand Up @@ -640,7 +640,7 @@ List recent traces in a project
- `-q, --query <value> - Search query (Sentry search syntax)`
- `-s, --sort <value> - Sort by: date, duration - (default: "date")`
- `-c, --cursor <value> - Pagination cursor (use "last" to continue from previous page)`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand All @@ -651,7 +651,7 @@ View details of a specific trace
**Flags:**
- `-w, --web - Open in browser`
- `--spans <value> - Span tree depth limit (number, "all" for unlimited, "no" to disable) - (default: "3")`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand All @@ -664,7 +664,7 @@ View logs associated with a trace
- `-t, --period <value> - Time period to search (e.g., "14d", "7d", "24h"). Default: 14d - (default: "14d")`
- `-n, --limit <value> - Number of log entries (1-1000) - (default: "100")`
- `-q, --query <value> - Additional filter query (Sentry search syntax)`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand Down Expand Up @@ -716,7 +716,7 @@ List issues in a project
- `-s, --sort <value> - Sort by: date, new, freq, user - (default: "date")`
- `-t, --period <value> - Time period for issue activity (e.g. 24h, 14d, 90d) - (default: "90d")`
- `-c, --cursor <value> - Pagination cursor for <org>/ or multi-target modes (use "last" to continue)`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--compact - Single-line rows for compact output (auto-detects if omitted)`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`
Expand All @@ -731,7 +731,7 @@ List organizations

**Flags:**
- `-n, --limit <value> - Maximum number of organizations to list - (default: "30")`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand All @@ -747,7 +747,7 @@ List projects
- `-n, --limit <value> - Maximum number of projects to list - (default: "30")`
- `-c, --cursor <value> - Pagination cursor (use "last" to continue from previous page)`
- `-p, --platform <value> - Filter by platform (e.g., javascript, python)`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand All @@ -762,7 +762,7 @@ List repositories
**Flags:**
- `-n, --limit <value> - Maximum number of repositories to list - (default: "30")`
- `-c, --cursor <value> - Pagination cursor (use "last" to continue from previous page)`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand All @@ -777,7 +777,7 @@ List teams
**Flags:**
- `-n, --limit <value> - Maximum number of teams to list - (default: "30")`
- `-c, --cursor <value> - Pagination cursor (use "last" to continue from previous page)`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand All @@ -794,7 +794,7 @@ List logs from a project
- `-q, --query <value> - Filter query (Sentry search syntax)`
- `-f, --follow <value> - Stream logs (optionally specify poll interval in seconds)`
- `--trace <value> - Filter logs by trace ID (32-character hex string)`
- `--fresh - Bypass cache and fetch fresh data`
- `--fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand All @@ -811,7 +811,7 @@ List recent traces in a project
- `-q, --query <value> - Search query (Sentry search syntax)`
- `-s, --sort <value> - Sort by: date, duration - (default: "date")`
- `-c, --cursor <value> - Pagination cursor (use "last" to continue from previous page)`
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand All @@ -836,7 +836,7 @@ Show the currently authenticated user
Show the currently authenticated user

**Flags:**
- `-f, --fresh - Bypass cache and fetch fresh data`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `--json - Output as JSON`
- `--fields <value> - Comma-separated fields to include in JSON output (dot.notation supported)`

Expand Down
32 changes: 32 additions & 0 deletions src/lib/db/dsn-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,30 @@ import { runUpsert } from "./utils.js";
/** Cache TTL in milliseconds (24 hours) */
const CACHE_TTL_MS = 24 * 60 * 60 * 1000;

/**
* Module-level flag to disable DSN cache reads.
* When true, getCachedDsn() and getCachedDetection() return undefined.
* Cache writes still proceed so the re-scanned result gets stored.
*/
let dsnCacheDisabled = false;

/**
* Disable DSN cache reads for this invocation.
* Called when `--fresh` is set to force a full re-scan.
*/
export function disableDsnCache(): void {
dsnCacheDisabled = true;
}

/**
* Re-enable DSN cache reads after `disableDsnCache()` was called.
* Only needed in tests to prevent one test's `--fresh` flag from
* leaking into subsequent tests.
*/
export function enableDsnCache(): void {
dsnCacheDisabled = false;
}

/** Row type matching the dsn_cache table schema (including v4 columns) */
type DsnCacheRow = {
directory: string;
Expand Down Expand Up @@ -116,6 +140,10 @@ function touchCacheEntry(directory: string): void {
export async function getCachedDsn(
directory: string
): Promise<CachedDsnEntry | undefined> {
if (dsnCacheDisabled) {
return;
}

const db = getDatabase();

const row = db
Expand Down Expand Up @@ -301,6 +329,10 @@ async function validateDirMtimes(
export async function getCachedDetection(
projectRoot: string
): Promise<CachedDetection | undefined> {
if (dsnCacheDisabled) {
return;
}

const db = getDatabase();

const row = db
Expand Down
25 changes: 22 additions & 3 deletions src/lib/dsn/code-scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { logger } from "../logger.js";
import { withTracingSpan } from "../telemetry.js";
import { createDetectedDsn, inferPackagePath, parseDsn } from "./parser.js";
import type { DetectedDsn } from "./types.js";
import { MONOREPO_ROOTS } from "./types.js";

/** Scoped logger for DSN code scanning */
const log = logger.withTag("dsn-scan");
Expand Down Expand Up @@ -61,9 +62,12 @@ const CONCURRENCY_LIMIT = 50;
/**
* Maximum depth to scan from project root.
* Depth 0 = files in root directory
* Depth 2 = files in second-level subdirectories (e.g., src/lib/file.ts)
* Depth 3 = files in third-level subdirectories (e.g., src/lib/config/sentry.ts)
*
* In monorepos, depth resets to 0 when entering a package directory
* (e.g., packages/spotlight/), giving each package its own depth budget.
*/
const MAX_SCAN_DEPTH = 2;
const MAX_SCAN_DEPTH = 3;

/**
* Directories that are always skipped regardless of .gitignore.
Expand Down Expand Up @@ -184,6 +188,19 @@ const normalizePath: (p: string) => string =
? (x) => x
: (x) => x.replaceAll(path.sep, path.posix.sep);

/**
* Check if a relative path is a monorepo package directory.
* Returns true for paths like "packages/frontend", "apps/server", etc.
* (exactly 2 segments where the first matches a MONOREPO_ROOTS entry)
*/
function isMonorepoPackageDir(relativePath: string): boolean {
const segments = relativePath.split("/");
return (
segments.length === 2 &&
MONOREPO_ROOTS.includes(segments[0] as (typeof MONOREPO_ROOTS)[number])
);
}

/**
* Pattern to match Sentry DSN URLs.
* Captures the full DSN including protocol, public key, optional secret key, host, and project ID.
Expand Down Expand Up @@ -441,6 +458,7 @@ async function collectFiles(cwd: string, ig: Ignore): Promise<CollectResult> {
const files: string[] = [];
const dirMtimes: Record<string, number> = {};

// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: recursive directory walk is inherently complex but straightforward
async function walk(dir: string, depth: number): Promise<void> {
if (depth > MAX_SCAN_DEPTH) {
return;
Expand All @@ -462,7 +480,8 @@ async function collectFiles(cwd: string, ig: Ignore): Promise<CollectResult> {
}

if (entry.isDirectory()) {
await walk(fullPath, depth + 1);
const nextDepth = isMonorepoPackageDir(relativePath) ? 0 : depth + 1;
await walk(fullPath, nextDepth);
} else if (entry.isFile() && shouldScanFile(entry.name)) {
files.push(relativePath);
}
Expand Down
2 changes: 2 additions & 0 deletions src/lib/dsn/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
// Cache Management
export {
clearDsnCache,
disableDsnCache,
enableDsnCache,
getCachedDsn,
setCachedDsn,
updateCachedResolution,
Expand Down
5 changes: 5 additions & 0 deletions src/lib/dsn/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,9 @@ export const MONOREPO_ROOTS = [
"libs",
"services",
"modules",
"projects",
"plugins",
"sites",
"workers",
"functions",
] as const;
4 changes: 3 additions & 1 deletion src/lib/list-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type { Aliases, Command, CommandContext } from "@stricli/core";
import type { SentryContext } from "../context.js";
import { parseOrgProjectArg } from "./arg-parsing.js";
import { buildCommand, numberParser } from "./command.js";
import { disableDsnCache } from "./dsn/index.js";
import { warning } from "./formatters/colors.js";
import type { CommandOutput, OutputConfig } from "./formatters/output.js";
import {
Expand Down Expand Up @@ -110,7 +111,7 @@ export const LIST_JSON_FLAG = {
*/
export const FRESH_FLAG = {
kind: "boolean" as const,
brief: "Bypass cache and fetch fresh data",
brief: "Bypass cache, re-detect projects, and fetch fresh data",
default: false,
} as const;

Expand Down Expand Up @@ -140,6 +141,7 @@ export const FRESH_ALIASES = { f: "fresh" } as const;
export function applyFreshFlag(flags: { readonly fresh: boolean }): void {
if (flags.fresh) {
disableResponseCache();
disableDsnCache();
}
}

Expand Down
Loading
Loading