Dashboard › cli › Distillation
96c34bdc-3059-400c-83b8-7abb907502a0["lore_tm_v1_eoKN4n3eTC2fzhe9zYTirNEMY-vKbigfBYxRZsY5eN4"]
Date: Sep 8, 2026
/home/byk/Code/getsentry/cli-pr-1558/packages/cli is Sentry CLI, built with Node.js, pnpm, and Stricli; packages/cli contains the CLI and apps/cli-docs contains the Astro + Starlight documentation site.gh-style developer UX; predictable JSON output for coding agents; and fast native binaries through Node SEA (fossilize) with SQLite API-response caching. Key features include DSN scanning for JavaScript, Python, Go, Java, Ruby, and PHP; project-root detection; directory-name inference; multi-region fan-out to us.sentry.io and de.sentry.io; monorepo aliases; issue explain and issue plan; and OAuth Device Flow..cursor/rules/ultracite.mdc before working in the codebase.packages/cli/CONTRIBUTING.md states list commands use optional positional context and gh CLI conventions: <org>/<project> selects an explicit organization/project, <org>/ selects all projects in an organization, <project> searches across accessible organizations, and omission triggers DSN/config auto-detection.<org>/<project> always wins, 2. config defaults set with sentry config set, 3. DSN auto-detection from SENTRY_DSN or source code.org view and project view accept optional positional context; issue view requires an issue ID; event view requires an event ID; every view command supports -w/--web; all view/list commands support --json; list commands support --limit.packages/cli/CONTRIBUTING.md: 1. choose list or view positional pattern, 2. reuse resolveOrg() or resolveOrgAndProject() from lib/resolve-target.ts, 3. support JSON output, 4. support -w/--web for views, 5. use ContextError for missing context, 6. add E2E tests under test/e2e/.yield new CommandOutput(data); formatting belongs in src/lib/formatters/<domain>.ts, and command files over roughly 400 lines should extract formatter helpers. stderr.write() is banned in command files; diagnostics use logger.buildCommand functions must be async *func() generators that yield new CommandOutput(data) and may return { hint }; output.human must receive the same data serialized to JSON. The wrapper injects --json and --fields, so commands must not define their own JSON flag, call stdout.write(), or branch on flags.json.[<org>/<project>/]<id> parsing uses parseSlashSeparatedArg and parseOrgProjectArg from src/lib/arg-parsing.ts; parsed modes are "auto-detect", "explicit", "project-search", and "org-all".mdKvTable(), colorTag(), escapeMarkdownCell(), and renderMarkdown(); raw muted()/chalk must not be embedded in output strings. Non-Markdown trees use isPlainOutput() ? text : muted(text). Plain-output precedence is SENTRY_PLAIN_OUTPUT > NO_COLOR > FORCE_COLOR when TTY > !isTTY; implementation is in src/lib/formatters/plain-detect.ts.buildDeleteCommand() from src/lib/mutate-command.ts; it injects --yes, --force, and --dry-run with aliases -y, -f, and -n, and refuses non-TTY execution without --yes/--force unless dry-run is active. Supporting utilities are isConfirmationBypassed(), guardNonInteractive(), confirmByTyping(), requireExplicitTarget(), DESTRUCTIVE_FLAGS, and DESTRUCTIVE_ALIASES.DRY_RUN_FLAG and DRY_RUN_ALIASES from src/lib/mutate-command.ts for standardized dry-run behavior.buildOrgListCommand for simple org-scoped lists such as team/repo, 2. dispatchOrgScopedList with overrides for complex project/issue lists, 3. buildListCommand with manual pagination for trace/span/dashboard lists.dispatchOrgScopedList overrides, handlers receive narrowed HandlerContext<T> and access ctx.parsed; resolveCursor() belongs inside the "org-all" closure; project-search omissions use "Project" as the ContextError resource; every dispatch must set orgSlugMatchBehavior to "redirect" or "error" explicitly.validateHexId(value, label) for 32-character trace/log IDs, validateSpanId(value) for 16-character span IDs, and validateTraceId(value) in src/lib/trace-id.ts; validators strip UUID dashes where applicable, normalize to lowercase, and throw ValidationError for malformed input."date", not "time"; sort types should be exported by the API layer, such as SpanSortValue from api/traces.ts.src/types/*.ts, with InferOutput, safeParse(), and re-exports from src/types/index.ts; when @sentry/api already provides response types, those must be imported instead of creating redundant schemas in src/types/sentry.ts.upsert() or runUpsert() from src/lib/db/utils.ts; upsert() supports conflict-key arrays and excludeFromUpdate.CliError from src/lib/errors.ts and use EXIT.* constants rather than hardcoded numeric exit codes. Error mapping includes AuthError 10β12, HostScopeError 13, ConfigError 20, ValidationError 21, ContextError 22, ResolutionError 23, ApiError 30, TimeoutError 31, SeerError 40β42, UpgradeError 50, DeviceFlowError 51, OutputError 60, and WizardError 61β64.ContextError; provided-but-not-found values use ResolutionError; malformed values use ValidationError. A ContextError command must be a single-line usage string, alternatives should be [] when defaults are irrelevant, and plural resources use " and " for correct grammar. pnpm run check:errors enforces these conventions.fuzzyMatch() from src/lib/fuzzy.ts and show at most 5 similar candidates rather than listing every entity.catch blocks are prohibited in production src/: each catch must rethrow, log with log.debug()/log.warn(), or log before returning a fallback. The no-silent-catch GritQL/Biome rule covers statement and .catch() forms; grandfathered catches use justified // biome-ignore lint/plugin: ... lines, and the suppression must be removed when logging/rethrowing is added because orphaned suppressions fail as suppressions/unused.looksLikeIssueShortId(), SPAN_ID_RE, HEX_ID_RE, or non-hex checks; resolve to the intended entity; warn through log.warn(); and return a hint for the correct command. Ambiguous cases retain an error with entity-aware suggestions.getAuthToken(), isAuthenticated(), and setAuthToken(token, expiresIn).src/lib/*.ts, agents must check existing modules; if an existing module covers at least 80% of the need, it should be extended instead. Every new src/lib/**/*.ts file requires module-level JSDoc..js; imports are grouped external-first then local; type-only imports use import type.queryEvents uses project:<slug> through buildProjectQuery(); replay index listReplays uses projectSlugs; issue index listIssuesPaginated uses either its project parameter or query string depending on mode. New explore datasets belong in resolveDatasetConfig.*.property.test.ts under test/lib/, *.model-based.test.ts under test/lib/db/, mirrored *.test.ts unit tests under test/, and E2E tests under test/e2e/.useTestConfigDir() from test/helpers.ts; tests must not delete process.env.SENTRY_CONFIG_DIR, capture it at module scope, or manually manage it in beforeEach/afterEach. TEST_TMP_DIR is namespaced by VITEST_POOL_ID.DEFAULT_NUM_RUNS from test/model-based/helpers.ts, whose configured value is 50; stateful tests use createIsolatedDbContext() with cleanup in finally.gh pr list --state open --search "in:title <file-or-symbol>" and gh issue list --state all --search "<symbol>", extending/commenting on existing work instead of duplicating it; 2. rebase onto current main and verify the bug still exists before review; 3. separate correctness defects from subjective UX changes; 4. reuse shared helpers such as autoPaginate() and safeParseJson() rather than hand-rolling fixes..lore.md in the project root and managed through lore (https://github.com/BYK/loreai).