Skip to content

Commit 2135d10

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/dashboard-widget-commands
2 parents 31d407f + 28ab647 commit 2135d10

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+2403
-675
lines changed

AGENTS.md

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,8 @@ All non-trivial human output must use the markdown rendering pipeline:
283283
- Build markdown strings with helpers: `mdKvTable()`, `colorTag()`, `escapeMarkdownCell()`, `renderMarkdown()`
284284
- **NEVER** use raw `muted()` / chalk in output strings — use `colorTag("muted", text)` inside markdown
285285
- Tree-structured output (box-drawing characters) that can't go through `renderMarkdown()` should use the `plainSafeMuted` pattern: `isPlainOutput() ? text : muted(text)`
286-
- `isPlainOutput()` precedence: `SENTRY_PLAIN_OUTPUT` > `NO_COLOR` > `FORCE_COLOR` > `!isTTY`
286+
- `isPlainOutput()` precedence: `SENTRY_PLAIN_OUTPUT` > `NO_COLOR` > `FORCE_COLOR` (TTY only) > `!isTTY`
287+
- `isPlainOutput()` lives in `src/lib/formatters/plain-detect.ts` (re-exported from `markdown.ts` for compat)
287288

288289
Reference: `formatters/trace.ts` (`formatAncestorChain`), `formatters/human.ts` (`plainSafeMuted`)
289290

@@ -781,4 +782,73 @@ mock.module("./some-module", () => ({
781782
| Add documentation | `docs/src/content/docs/` |
782783
783784
<!-- This section is maintained by the coding agent via lore (https://github.com/BYK/opencode-lore) -->
785+
## Long-term Knowledge
786+
787+
### Architecture
788+
789+
<!-- lore:019ce2be-39f1-7ad9-a4c5-4506b62f689c -->
790+
* **api-client.ts split into domain modules under src/lib/api/**: The original monolithic \`src/lib/api-client.ts\` (1,977 lines) was split into 12 focused domain modules under \`src/lib/api/\`: infrastructure.ts (shared helpers, types, raw requests), organizations.ts, projects.ts, teams.ts, repositories.ts, issues.ts, events.ts, traces.ts, logs.ts, seer.ts, trials.ts, users.ts. The original \`api-client.ts\` was converted to a ~100-line barrel re-export file preserving all existing import paths. The \`biome.jsonc\` override for \`noBarrelFile\` already includes \`api-client.ts\`. When adding new API functions, place them in the appropriate domain module under \`src/lib/api/\`, not in the barrel file.
791+
792+
<!-- lore:019cb8ea-c6f0-75d8-bda7-e32b4e217f92 -->
793+
* **CLI telemetry DSN is public write-only — safe to embed in install script**: The CLI's Sentry DSN (\`SENTRY\_CLI\_DSN\` in \`src/lib/constants.ts\`) is a public write-only ingest key already baked into every binary. Safe to hardcode in install scripts. Opt-out: \`SENTRY\_CLI\_NO\_TELEMETRY=1\`.
794+
795+
<!-- lore:019c978a-18b5-7a0d-a55f-b72f7789bdac -->
796+
* **cli.sentry.dev is served from gh-pages branch via GitHub Pages**: \`cli.sentry.dev\` is served from gh-pages branch via GitHub Pages. Craft's gh-pages target runs \`git rm -r -f .\` before extracting docs — persist extra files via \`postReleaseCommand\` in \`.craft.yml\`. Install script supports \`--channel nightly\`, downloading from the \`nightly\` release tag directly. version.json is only used by upgrade/version-check flow.
797+
798+
<!-- lore:019cbe93-19b8-7776-9705-20bbde226599 -->
799+
* **Nightly delta upgrade buildNightlyPatchGraph fetches ALL patch tags — O(N) HTTP calls**: Delta upgrade in \`src/lib/delta-upgrade.ts\` supports stable (GitHub Releases) and nightly (GHCR) channels. \`filterAndSortChainTags\` filters \`patch-\*\` tags by version range using \`Bun.semver.order()\`. GHCR uses \`fetchWithRetry\` (10s timeout + 1 retry; blobs 30s) with optional \`signal?: AbortSignal\` combined via \`AbortSignal.any()\`. \`isExternalAbort(error, signal)\` skips retries for external aborts — critical for background prefetch. Patches cached to \`~/.sentry/patch-cache/\` (file-based, 7-day TTL). \`loadCachedChain\` stitches patches for multi-hop offline upgrades.
800+
801+
<!-- lore:2c3eb7ab-1341-4392-89fd-d81095cfe9c4 -->
802+
* **npm bundle requires Node.js >= 22 due to node:sqlite polyfill**: The npm package (dist/bin.cjs) requires Node.js >= 22 because the bun:sqlite polyfill uses \`node:sqlite\`. A runtime version guard in the esbuild banner catches this early. When writing esbuild banner strings in TS template literals, double-escape: \`\\\\\\\n\` in TS → \`\\\n\` in output → newline at runtime. Single \`\\\n\` produces a literal newline inside a JS string, causing SyntaxError.
803+
804+
<!-- lore:019c972c-9f0f-75cd-9e24-9bdbb1ac03d6 -->
805+
* **Numeric issue ID resolution returns org:undefined despite API success**: Numeric issue ID resolution in \`resolveNumericIssue()\`: (1) try DSN/env/config for org, (2) if found use \`getIssueInOrg(org, id)\` with region routing, (3) else fall back to unscoped \`getIssue(id)\`, (4) extract org from \`issue.permalink\` via \`parseSentryUrl\` as final fallback. \`parseSentryUrl\` handles path-based (\`/organizations/{org}/...\`) and subdomain-style URLs. \`matchSubdomainOrg()\` filters region subdomains by requiring slug length > 2. Self-hosted uses path-based only.
806+
807+
<!-- lore:019ce0bb-f35d-7380-b661-8dc56f9938cf -->
808+
* **Seer trial prompt uses middleware layering in bin.ts error handling chain**: The CLI's error recovery middlewares in \`bin.ts\` are layered: \`main() → executeWithAutoAuth() → executeWithSeerTrialPrompt() → runCommand()\`. Seer trial prompts (for \`no\_budget\`/\`not\_enabled\` errors) are caught by the inner wrapper; auth errors bubble up to the outer wrapper. After successful auth login retry, the retry also goes through \`executeWithSeerTrialPrompt\` (not \`runCommand\` directly) so the full middleware chain applies. Trial check API: \`GET /api/0/customers/{org}/\`\`productTrials\[]\` (prefer \`seerUsers\`, fallback \`seerAutofix\`). Start trial: \`PUT /api/0/customers/{org}/product-trial/\`. The \`/customers/\` endpoint is getsentry SaaS-only; self-hosted 404s gracefully. \`ai\_disabled\` errors are excluded (admin's explicit choice). \`startSeerTrial\` accepts \`category\` from the trial object — don't hardcode it.
809+
810+
### Decision
811+
812+
<!-- lore:019c99d5-69f2-74eb-8c86-411f8512801d -->
813+
* **Raw markdown output for non-interactive terminals, rendered for TTY**: Markdown-first output pipeline: custom renderer in \`src/lib/formatters/markdown.ts\` walks \`marked\` tokens to produce ANSI-styled output. Commands build CommonMark using helpers (\`mdKvTable()\`, \`mdRow()\`, \`colorTag()\`, \`escapeMarkdownCell()\`, \`safeCodeSpan()\`) and pass through \`renderMarkdown()\`. \`isPlainOutput()\` precedence: \`SENTRY\_PLAIN\_OUTPUT\` > \`NO\_COLOR\` > \`FORCE\_COLOR\` > \`!isTTY\`. \`--json\` always outputs JSON. Colors defined in \`COLORS\` object in \`colors.ts\`. Tests run non-TTY so assertions match raw CommonMark; use \`stripAnsi()\` helper for rendered-mode assertions.
814+
815+
<!-- lore:00166785-609d-4ab5-911e-ee205d17b90c -->
816+
* **whoami should be separate from auth status command**: The \`sentry auth whoami\` command should be a dedicated command separate from \`sentry auth status\`. They serve different purposes: \`status\` shows everything about auth state (token, expiry, defaults, org verification), while \`whoami\` just shows user identity (name, email, username, ID) by fetching live from \`/auth/\` endpoint. \`sentry whoami\` should be a top-level alias (like \`sentry issues\`\`sentry issue list\`). \`whoami\` should support \`--json\` for machine consumption and be lightweight — no credential verification, no defaults listing.
817+
818+
### Gotcha
819+
820+
<!-- lore:019c8ab6-d119-7365-9359-98ecf464b704 -->
821+
* **@sentry/api SDK passes Request object to custom fetch — headers lost on Node.js**: @sentry/api SDK calls \`\_fetch(request)\` with no init object. In \`authenticatedFetch\`, \`init\` is undefined so \`prepareHeaders\` creates empty headers — on Node.js this strips Content-Type (HTTP 415). Fix: fall back to \`input.headers\` when \`init\` is undefined. Use \`unwrapPaginatedResult\` (not \`unwrapResult\`) to access the Response's Link header for pagination. \`per\_page\` is not in SDK types; cast query to pass it at runtime.
822+
823+
<!-- lore:019c9e98-7af4-7e25-95f4-fc06f7abf564 -->
824+
* **Bun binary build requires SENTRY\_CLIENT\_ID env var**: The build script (\`script/bundle.ts\`) requires \`SENTRY\_CLIENT\_ID\` environment variable and exits with code 1 if missing. When building locally, use \`bun run --env-file=.env.local build\` or set the env var explicitly. The binary build (\`bun run build\`) also needs it. Without it you get: \`Error: SENTRY\_CLIENT\_ID environment variable is required.\`
825+
826+
<!-- lore:019c9776-e3dd-7632-88b8-358a19506218 -->
827+
* **GitHub immutable releases prevent rolling nightly tag pattern**: getsentry/cli has immutable GitHub releases — assets can't be modified and tags can NEVER be reused. Nightly builds publish to GHCR with versioned tags like \`nightly-0.14.0-dev.1772661724\`, not GitHub Releases or npm. \`fetchManifest()\` throws \`UpgradeError("network\_error")\` for both network failures and non-200 — callers must check message for HTTP 404/403. Craft with no \`preReleaseCommand\` silently skips \`bump-version.sh\` if only target is \`github\`.
828+
829+
<!-- lore:019cb8c2-d7b5-780c-8a9f-d20001bc198f -->
830+
* **Install script: BSD sed and awk JSON parsing breaks OCI digest extraction**: The install script parses OCI manifests with awk (no jq). Key trap: BSD sed \`\n\` is literal, not newline. Fix: single awk pass tracking last-seen \`"digest"\`, printing when \`"org.opencontainers.image.title"\` matches target. The config digest (\`sha256:44136fa...\`) is a 2-byte \`{}\` blob — downloading it instead of the real binary causes \`gunzip: unexpected end of file\`.
831+
832+
<!-- lore:019c969a-1c90-7041-88a8-4e4d9a51ebed -->
833+
* **Multiple mockFetch calls replace each other — use unified mocks for multi-endpoint tests**: Bun test mocking gotchas: (1) \`mockFetch()\` replaces \`globalThis.fetch\` — calling it twice replaces the first mock. Use a single unified fetch mock dispatching by URL pattern. (2) \`mock.module()\` pollutes the module registry for ALL subsequent test files. Tests using it must live in \`test/isolated/\` and run via \`test:isolated\`. This also causes \`delta-upgrade.test.ts\` to fail when run alongside \`test/isolated/delta-upgrade.test.ts\` — the isolated test's \`mock.module()\` replaces \`CLI\_VERSION\` for all subsequent files. (3) For \`Bun.spawn\`, use direct property assignment in \`beforeEach\`/\`afterEach\`.
834+
835+
<!-- lore:019c9741-d78e-73b1-87c2-e360ef6c7475 -->
836+
* **useTestConfigDir without isolateProjectRoot causes DSN scanning of repo tree**: \`useTestConfigDir()\` creates temp dirs under \`.test-tmp/\` in the repo tree. Without \`{ isolateProjectRoot: true }\`, \`findProjectRoot\` walks up and finds the repo's \`.git\`, causing DSN detection to scan real source code and trigger network calls against test mocks (timeouts). Always pass \`isolateProjectRoot: true\` when tests exercise \`resolveOrg\`, \`detectDsn\`, or \`findProjectRoot\`.
837+
838+
### Pattern
839+
840+
<!-- lore:019c972c-9f11-7c0d-96ce-3f8cc2641175 -->
841+
* **Org-scoped SDK calls follow getOrgSdkConfig + unwrapResult pattern**: All org-scoped API calls in src/lib/api-client.ts: (1) call \`getOrgSdkConfig(orgSlug)\` for regional URL + SDK config, (2) spread into SDK function: \`{ ...config, path: { organization\_id\_or\_slug: orgSlug, ... } }\`, (3) pass to \`unwrapResult(result, errorContext)\`. Shared helpers \`resolveAllTargets\`/\`resolveOrgAndProject\` must NOT call \`fetchProjectId\` — commands that need it enrich targets themselves.
842+
843+
<!-- lore:5ac4e219-ea1f-41cb-8e97-7e946f5848c0 -->
844+
* **PR workflow: wait for Seer and Cursor BugBot before resolving**: After pushing a PR in the getsentry/cli repo, the CI pipeline includes Seer Code Review and Cursor Bugbot as advisory checks. Both typically take 2-3 minutes but may not trigger on draft PRs — only ready-for-review PRs reliably get bot reviews. The workflow is: push → wait for all CI (including npm build jobs which test the actual bundle) → check for inline review comments from Seer/BugBot → fix if needed → repeat. Use \`gh pr checks \<PR> --watch\` to monitor. Review comments are fetched via \`gh api repos/OWNER/REPO/pulls/NUM/comments\` and \`gh api repos/OWNER/REPO/pulls/NUM/reviews\`.
845+
846+
<!-- lore:019cb162-d3ad-7b05-ab4f-f87892d517a6 -->
847+
* **Shared pagination infrastructure: buildPaginationContextKey and parseCursorFlag**: List commands with cursor pagination use \`buildPaginationContextKey(type, identifier, flags)\` for composite context keys and \`parseCursorFlag(value)\` accepting \`"last"\` magic value. Critical: \`resolveCursor()\` must be called inside the \`org-all\` override closure, not before \`dispatchOrgScopedList\` — otherwise cursor validation errors fire before the correct mode-specific error.
848+
849+
<!-- lore:019cbd5f-ec35-7e2d-8386-6d3a67adf0cf -->
850+
* **Telemetry instrumentation pattern: withTracingSpan + captureException for handled errors**: For graceful-fallback operations, use \`withTracingSpan\` from \`src/lib/telemetry.ts\` for child spans and \`captureException\` from \`@sentry/bun\` (named import — Biome forbids namespace imports) with \`level: 'warning'\` for non-fatal errors. \`withTracingSpan\` uses \`onlyIfParent: true\` — no-op without active transaction. User-visible fallbacks use \`log.warn()\` not \`log.debug()\`. Several commands bypass telemetry by importing \`buildCommand\` from \`@stricli/core\` directly instead of \`../../lib/command.js\` (trace/list, trace/view, log/view, api.ts, help.ts).
851+
852+
<!-- lore:019cc43d-e651-7154-a88e-1309c4a2a2b6 -->
853+
* **Testing Stricli command func() bodies via spyOn mocking**: To unit-test a Stricli command's \`func()\` body: (1) \`const func = await cmd.loader()\`, (2) \`func.call(mockContext, flags, ...args)\` with mock \`stdout\`, \`stderr\`, \`cwd\`, \`setContext\`. (3) \`spyOn\` namespace imports to mock dependencies (e.g., \`spyOn(apiClient, 'getLogs')\`). The \`loader()\` return type union causes \`.call()\` LSP errors — these are false positives that pass \`tsc --noEmit\`. When API functions are renamed (e.g., \`getLog\`\`getLogs\`), update both spy target name AND mock return shape (single → array). Slug normalization (\`normalizeSlug\`) replaces underscores with dashes but does NOT lowercase — test assertions must match original casing (e.g., \`'CAM-82X'\` not \`'cam-82x'\`).
784854
<!-- End lore-managed section -->

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,30 @@
11
# Changelog
22

33
<!-- Craft will auto-populate this file -->
4+
## 0.18.0
5+
6+
### New Features ✨
7+
8+
- (span) Make span list dual-mode and add --period flag by @BYK in [#461](https://github.com/getsentry/cli/pull/461)
9+
- Refactor SKILL.md into modular reference files by @BYK in [#458](https://github.com/getsentry/cli/pull/458)
10+
11+
### Bug Fixes 🐛
12+
13+
- (constants) Normalize bare hostnames in SENTRY_HOST/SENTRY_URL by @BYK in [#467](https://github.com/getsentry/cli/pull/467)
14+
- (dsn) Treat EISDIR and ENOTDIR as ignorable file errors by @BYK in [#464](https://github.com/getsentry/cli/pull/464)
15+
- (test) Use os.tmpdir() for test temp directories by @BYK in [#457](https://github.com/getsentry/cli/pull/457)
16+
- Make piped output human-readable instead of raw CommonMark by @BYK in [#462](https://github.com/getsentry/cli/pull/462)
17+
- Clean up upgrade output and hide empty table headers by @BYK in [#459](https://github.com/getsentry/cli/pull/459)
18+
- Improve error messages — fix ContextError/ResolutionError misuse by @BYK in [#456](https://github.com/getsentry/cli/pull/456)
19+
20+
### Documentation 📚
21+
22+
- Add key principles and API schema workflow to agent guidance by @BYK in [#466](https://github.com/getsentry/cli/pull/466)
23+
24+
### Internal Changes 🔧
25+
26+
- (list) Align all list commands to issue list standards by @BYK in [#453](https://github.com/getsentry/cli/pull/453)
27+
428
## 0.17.0
529

630
### New Features ✨

biome.jsonc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,12 @@
6262
// db/index.ts exports db connection utilities - not a barrel file but triggers the rule
6363
// api-client.ts is a barrel that re-exports from src/lib/api/ domain modules
6464
// to preserve the existing import path for all consumers
65-
"includes": ["src/lib/db/index.ts", "src/lib/api-client.ts"],
65+
// markdown.ts re-exports isPlainOutput from plain-detect.ts for backward compat
66+
"includes": [
67+
"src/lib/db/index.ts",
68+
"src/lib/api-client.ts",
69+
"src/lib/formatters/markdown.ts"
70+
],
6671
"linter": {
6772
"rules": {
6873
"performance": {

docs/src/content/docs/agent-guidance.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ description: Operational guidance for AI coding agents using the Sentry CLI
55

66
Best practices and operational guidance for AI coding agents using the Sentry CLI.
77

8+
## Key Principles
9+
10+
- **Prefer CLI commands over raw API calls** — the CLI has dedicated commands for most tasks. Reach for `sentry issue view`, `sentry issue list`, `sentry trace view`, etc. before constructing API calls manually or fetching external documentation.
11+
- **Use `sentry schema` to explore the API** — if you need to discover API endpoints, run `sentry schema` to browse interactively or `sentry schema <resource>` to search. This is faster than fetching OpenAPI specs externally.
12+
- **Use `sentry issue view <id>` to investigate issues** — when asked about a specific issue (e.g., `CLI-G5`, `PROJECT-123`), use `sentry issue view` directly.
13+
- **Use `--json` for machine-readable output** — pipe through `jq` for filtering. Human-readable output includes formatting that is hard to parse.
14+
- **The CLI auto-detects org/project** — most commands work without explicit targets by scanning for DSNs in `.env` files and source code.
15+
816
## Design Principles
917

1018
The `sentry` CLI follows conventions from well-known tools — if you're familiar with them, that knowledge transfers directly:
@@ -71,6 +79,19 @@ sentry log list my-org/my-project --follow
7179
sentry log list my-org/my-project --query "severity:error"
7280
```
7381

82+
### Explore the API Schema
83+
84+
```bash
85+
# Browse all API resource categories
86+
sentry schema
87+
88+
# Search for endpoints related to a resource
89+
sentry schema issues
90+
91+
# Get details about a specific endpoint
92+
sentry schema "GET /api/0/organizations/{organization_id_or_slug}/issues/"
93+
```
94+
7495
### Arbitrary API Access
7596

7697
```bash
@@ -89,3 +110,4 @@ sentry api /api/0/organizations/my-org/projects/ --method POST --data '{"name":"
89110
- **Org/project ambiguity**: Auto-detection scans for DSNs in `.env` files and source code. If the project is ambiguous, specify explicitly: `sentry issue list my-org/my-project`.
90111
- **Confusing `--query` syntax**: The `--query` flag uses Sentry search syntax (e.g., `is:unresolved`, `assigned:me`), not free text search.
91112
- **Not using `--web`**: View commands support `-w`/`--web` to open the resource in the browser — useful for sharing links.
113+
- **Fetching API schemas instead of using the CLI**: Prefer `sentry schema` to browse the API and `sentry api` to make requests — the CLI handles authentication and endpoint resolution, so there's rarely a need to download OpenAPI specs separately.

docs/src/content/docs/agentic-usage.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,22 @@ With this skill, agents can:
2121

2222
- **View issues** - List and inspect Sentry issues from your projects
2323
- **Inspect events** - Look at specific error events and their details
24+
- **AI analysis** - Get root cause analysis and fix plans via Seer AI
2425
- **Browse projects** - List projects and organizations you have access to
26+
- **Explore the API** - Browse API endpoints with `sentry schema` and make arbitrary requests with `sentry api`
2527
- **Make API calls** - Execute arbitrary Sentry API requests
2628
- **Authenticate** - Help you set up CLI authentication
2729

2830
## How It Works
2931

30-
When you ask your agent about Sentry errors or want to investigate an issue, the agent can use this skill to fetch real data from your Sentry account. For example:
32+
When you ask your agent about Sentry errors or want to investigate an issue, the agent uses CLI commands to fetch real data from your Sentry account. For example:
3133

32-
- "Show me the latest issues in my project"
33-
- "What's the stack trace for ISSUE-123?"
34-
- "List all projects in my organization"
34+
- "Show me the latest issues in my project" → `sentry issue list`
35+
- "What's the stack trace for ISSUE-123?" → `sentry issue view ISSUE-123`
36+
- "List all projects in my organization" → `sentry project list my-org`
37+
- "What API endpoints exist for releases?" → `sentry schema releases`
38+
39+
The CLI has dedicated commands for most Sentry tasks, so agents should prefer `sentry` commands over constructing raw API calls. The `sentry schema` command provides built-in API exploration, and `sentry api` handles authenticated requests for anything not covered by a dedicated command.
3540

3641
The skill uses your existing CLI authentication, so you'll need to run `sentry auth login` first if you haven't already.
3742

0 commit comments

Comments
 (0)