Skip to content

Commit 9104325

Browse files
merge: resolve conflict with main (AGENTS.md lore → .lore.md)
2 parents 53b85f0 + 1c97cbf commit 9104325

100 files changed

Lines changed: 5242 additions & 789 deletions

File tree

Some content is hidden

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

.github/workflows/ci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ jobs:
193193
- run: bun run typecheck
194194
- run: bun run check:deps
195195
- run: bun run check:errors
196+
- run: bun run check:patches
196197

197198
test-unit:
198199
name: Unit Tests
@@ -633,6 +634,9 @@ jobs:
633634
with:
634635
bun-version: "1.3.13"
635636
- uses: pnpm/action-setup@v4
637+
- uses: actions/setup-node@v6
638+
with:
639+
node-version: "22"
636640
- uses: actions/cache@v5
637641
id: cache
638642
with:

.lore.md

Lines changed: 47 additions & 30 deletions
Large diffs are not rendered by default.

AGENTS.md

Lines changed: 1 addition & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,51 +1066,5 @@ mock.module("./some-module", () => ({
10661066
<!-- This section is maintained by the coding agent via lore (https://github.com/BYK/loreai) -->
10671067
## Long-term Knowledge
10681068

1069-
### Architecture
1070-
1071-
<!-- lore:019df992-599e-7bad-bf86-248dfda9c24b -->
1072-
* **sentry local command uses getParsedEnvelope() for envelope item dispatch**: \`src/commands/local.ts\` receives raw Sentry envelopes via a Hono HTTP server, pushes them into a Spotlight \`MessageBuffer\<EventContainer>\`, then in the subscriber calls \`container.getParsedEnvelope()\` to get \`\[header, items\[]]\`. Each item's \`\[itemHeader, itemPayload]\` is dispatched: \`event\`/\`error\` types → \`formatErrorItem\`, \`transaction\`\`formatTransactionItem\`, \`log\`\`formatLogItem\` (returns multiple lines), all others fall back to a minimal \`timestamp • type\` line. Formatters are inline in \`local.ts\` and use the CLI's color helpers.
1073-
1074-
<!-- lore:019da6b7-1d7b-70b0-adf8-769712f5c577 -->
1075-
* **Issue resolve --in grammar: release + @next + @commit sentinels**: \*\*Issue resolve --in grammar + repo\_cache SQLite table\*\*: \`sentry issue resolve --in\` grammar: (a) omitted→immediate, (b) \`\<version>\`\`inRelease\`, (c) \`@next\`\`inNextRelease\`, (d) \`@commit\`→auto-detect git HEAD via \`src/lib/git.ts\`, (e) \`@commit:\<repo>@\<sha>\`→explicit. \`parseResolveSpec\` splits on LAST \`@\` for scoped names. API requires \`statusDetails.inCommit: {commit, repository}\` — not bare SHA. Repo matching uses \`listRepositoriesCached(org)\` (7-day SQLite cache in \`repo\_cache\` table, schema v14). Always use \`listAllRepositories\` (paginated via \`API\_MAX\_PER\_PAGE\`) — never \`listRepositories\` (silently caps ~25). \`setCachedRepos\` wrapped in try/catch so read-only DBs (macOS \`sudo brew install\`) don't crash commands.
1076-
1077-
<!-- lore:019dd1bb-5546-79b2-8034-066acf076779 -->
1078-
* **Response cache hit invisibility — synthetic Response carries no marker**: Response cache hit invisibility — synthetic Response from \`getCachedResponse()\` in \`src/lib/response-cache.ts\` is indistinguishable from network. Solved via module-level \`lastCacheHitAgeMs\`: set on hit, cleared at top of \`authenticatedFetch()\` per-call (single-process CLI = race-free). \`src/lib/cache-hint.ts\` provides \`formatCacheHint()\` (\`"cached · 3m ago · use -f to refresh"\`) and \`appendCacheHint(existingHint)\` (joins with \` | \`). Wired in \`buildCommand\` (\`src/lib/command.ts\`): \`appendCacheHint(returned?.hint)\` runs only when generator returns a \`CommandReturn\` — bare \`return;\` paths (e.g. \`--web\`) skip the hint. Same chokepoint can host future cross-cutting hint decorators. Test-only \`\_setLastCacheHitAgeForTesting(ms)\` exposes state.
1079-
1080-
<!-- lore:019ce0bb-f35d-7380-b661-8dc56f9938cf -->
1081-
* **Seer trial prompt uses middleware layering in bin.ts error handling chain**: Seer trial prompt via error middleware layering: \`bin.ts\` chain is \`main() → executeWithAutoAuth() → executeWithSeerTrialPrompt() → runCommand()\`. Seer trial prompts (\`no\_budget\`/\`not\_enabled\`) caught by inner wrapper; auth errors bubble to outer. Trial API: \`GET /api/0/customers/{org}/\`\`productTrials\[]\` (prefer \`seerUsers\`, fallback \`seerAutofix\`). Start: \`PUT /api/0/customers/{org}/product-trial/\`. SaaS-only; self-hosted 404s gracefully. \`ai\_disabled\` excluded. \`startSeerTrial\` accepts \`category\` from trial object — don't hardcode.
1082-
1083-
### Decision
1084-
1085-
<!-- lore:019c99d5-69f2-74eb-8c86-411f8512801d -->
1086-
* **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.
1087-
1088-
### Gotcha
1089-
1090-
<!-- lore:019df992-5999-7f3a-b76e-29e3b08c354b -->
1091-
* **@spotlightjs/spotlight exports only two paths — no formatter/parser access**: The \`@spotlightjs/spotlight\` package's \`exports\` map exposes only \`.\` (main server) and \`./sdk\` (buffer API). Formatter registries (\`humanFormatters\`, \`applyFormatter\`) and parser helpers (\`isErrorEvent\`, type guards) live under \`dist/server/formatters/\` and \`dist/server/parser/\` but are not in \`exports\`. Bun's strict module resolution blocks deep \`dist/\` imports at runtime. Workaround: write inline formatters using the CLI's own color system (\`muted\`, \`bold\`, \`cyan\`, etc.) following the same pattern as Spotlight's human formatters.
1092-
1093-
<!-- lore:019db57b-ba0d-7a5f-803d-f15b7a819d05 -->
1094-
* **--json schema stability: collapse=organization drops nested org fields**: --json schema + response cache gotchas: (1) \`?collapse=organization\` shrinks \`organization\` to \`{id, slug}\` — silent --json regression. \`jsonTransform\` re-hydrates \`organization.name\` via \`resolveOrgDisplayName\` against \`org\_regions\` cache. (2) \`buildCacheKey()\` normalizes URL with sorted query params, so \`invalidateCachedResponse(baseUrl)\` misses entries with query suffixes. Use \`invalidateCachedResponsesMatching(prefix)\` (raw \`startsWith()\`); \`buildApiUrl()\` always emits trailing slash → safe prefix. (3) When \`jsonTransform\` is set, \`jsonExclude\` and \`filterFields\` are NOT applied — transform must call \`filterFields(result, fields)\` and omit excluded keys itself.
1095-
1096-
<!-- lore:019dd588-df59-71ab-aad6-7453c4a99ccb -->
1097-
* **API tests must use useTestConfigDir to isolate disk response cache**: \*\*API tests must use useTestConfigDir to isolate disk response cache\*\*: Tests mocking \`globalThis.fetch\` MUST call \`useTestConfigDir()\` + \`setAuthToken()\`. \`authenticatedFetch\` checks a filesystem response cache (\`~/.sentry/cache/responses/\`) BEFORE calling fetch — without per-test dirs, test N's response is served to test N+1. TTL tiers in \`classifyUrl()\`: stable=5min, volatile=60s (issues/logs), immutable=24h (events/traces by ID). Also: \`@sentry/api\` SDK calls \`\_fetch(request)\` with no init — fall back to \`input.headers\` when \`init\` is undefined (prevents HTTP 415). SDK returns \`data={}\` for empty/204 responses — always guard with \`Array.isArray(data)\` before \`.map()\`. Use \`unwrapPaginatedResult\` (not \`unwrapResult\`) for Link header pagination.
1098-
1099-
<!-- lore:019dc095-9ce4-7fe1-89ab-12efeffddcee -->
1100-
* **Biome noUselessUndefined also rejects () => {} empty arrow callbacks**: Biome lint traps: (1) \`noUselessUndefined\` rejects \`() => undefined\` AND \`noEmptyBlockStatements\` rejects \`() => {}\` — use top-level \`function noop(): void {}\`. (2) \`noExcessiveCognitiveComplexity\` caps at 15. (3) \`expect(() => fn()).toThrow(X)\` must be one line. (4) Plugin forbids raw \`metadata\` table queries — use \`getMetadata\`/\`setMetadata\`/\`clearMetadata\`. (5) Also enforced: \`useBlockStatements\`, \`noNestedTernary\`, \`useAtIndex\`, \`noStaticOnlyClass\`, \`useSimplifiedLogicExpression\`, \`noShadow\`. Namespace imports forbidden. (6) \`useYield\` fires on \`async \*func()\` with statements but not empty bodies — only add \`biome-ignore\` to generators with statements. \`lint:fix\` differs from CI \`lint\`: auto-fix hides \`noPrecisionLoss\` on >2^53 literals, \`noIncrementDecrement\`, import ordering. Always \`bun run lint\` before pushing.
1101-
1102-
<!-- lore:019dc60a-2c2c-7d07-b583-40c5a9342101 -->
1103-
* **Bun --isolate coverage inflates LF count for files with verbose comments/JSDoc**: Bun --isolate coverage inflates LF count: under \`bun test --isolate --parallel\` (CI's \`test:unit\`), Bun's coverage instrumentation counts comments, blank lines, type annotations, and closing braces as 'executable'. E.g. \`zstd-transport.ts\` LF=165 locally → 210 under --isolate, dropping coverage 99%→78%. Workaround: trim verbose inline comments inside function bodies; move rationale to JSDoc above the function. Statement coverage stays 100% — 'missing' lines are non-executable.
1104-
1105-
<!-- lore:019e0455-945e-77ac-ac2f-ca206985387a -->
1106-
* **Bun /$bunfs/ virtual FS uses JS parser — embedded .tsx files fail on TS syntax**: \*\*Bun \`/$bunfs/\` virtual FS + Ink TUI sidecar embedding\*\*: Files embedded via \`with { type: "file" }\` run from \`/$bunfs/root/\` using a JS parser (not TypeScript) — raw \`.tsx\` crashes on \`import { type Foo }\`. Fix: pre-bundle \`.tsx\`\`.js\` via esbuild before embedding (\`script/text-import-plugin.ts\`). \`/$bunfs/\` has no \`node\_modules\` — inline all deps; use \`createRequire\` banner for CJS deps. Only \`node:\*\` builtins external. Query strings in \`/$bunfs/\` paths cause ENOENT. Related: Ink TUI sidecar (\`ink-app.tsx\`) must be fully self-contained — main bundle must NOT import \`ink\`/\`react\` separately; call \`app.mountApp()\` from the sidecar only to avoid dual-React "Invalid hook call" errors.
1107-
1108-
<!-- lore:019dbabc-1b61-7768-ac90-e62d6464af34 -->
1109-
* **Bun 1.3.11 tty.ReadStream leaks libuv handle — process.stdin.unref is undefined**: Bun 1.3.11 macOS TTY bug: \`process.stdin\` via kqueue \`EVFILT\_READ\` fails to deliver keystrokes when fd 0 is inherited via \`exec bin \</dev/tty\` (curl|bash flow). Linux (epoll) works. Workaround: \`openSync('/dev/tty','r')\` + \`new tty.ReadStream(fd)\` routes through libuv threadpool. Lives in \`src/lib/init/stdin-reopen.ts\`, darwin-gated in \`wizard-runner.ts\` via \`using \_tty\`. Leaks libuv handle → safety net in \`init.ts\`: \`setTimeout(process.exit, 100).unref()\`. Skip under \`NODE\_ENV=test\`.
1110-
1111-
<!-- lore:019db776-111b-73db-b4ad-b762dfd4808f -->
1112-
* **MastraClient has no dispose API — use AbortController for cleanup**: MastraClient has no \`close()\`/\`dispose()\` API — cleanup via \`ClientOptions.abortSignal\` (constructor) or per-prompt \`signal\`. Without explicit abort, Bun's fetch dispatcher keep-alive sockets hold the event loop alive past natural exit. Pattern in \`src/lib/init/wizard-runner.ts\`: create \`AbortController\` per \`runWizard\`, pass \`abortSignal: controller.signal\` to \`new MastraClient(...)\`, abort via \`using \_ = { \[Symbol.dispose]: () => controller.abort() }\`. Custom \`fetch\` wrapper must preserve \`init.signal\` via spread. Tests capture \`ClientOptions\` via \`spyOn(MastraClient.prototype, 'getWorkflow').mockImplementation(function() { capturedOpts.push(this.options); ... })\`.
1113-
1114-
<!-- lore:019d0b04-ccec-7bd2-a5ca-732e7064cc1a -->
1115-
* **Multi-region fan-out: distinguish all-403 from empty orgs with hasSuccessfulRegion flag**: In \`listOrganizationsUncached\` (\`src/lib/api/organizations.ts\`), \`Promise.allSettled\` collects multi-region results. Don't use \`flatResults.length === 0\` to detect all-regions-failed — a region returning 200 OK with zero orgs pushes nothing into \`flatResults\`. Track a \`hasSuccessfulRegion\` boolean on any \`"fulfilled"\` settlement. Only re-throw 403 \`ApiError\` when \`!hasSuccessfulRegion && lastScopeError\`.
1069+
For long-term knowledge entries managed by [lore](https://github.com/BYK/loreai) (gotchas, patterns, decisions, architecture), see [`.lore.md`](.lore.md) in the project root.
11161070
<!-- End lore-managed section -->

MIGRATION-PLAN.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Bun → Node.js Migration Plan
2+
3+
Status: In progress. See below for completed and remaining steps.
4+
5+
## Completed
6+
7+
### Phase 4 (early): Package Manager Switch
8+
- [x] Changed `packageManager` from `bun@1.3.13` to `pnpm@10.11.0`
9+
- [x] Moved `patchedDependencies` into `pnpm` config section
10+
- [x] Added `onlyBuiltDependencies: ["esbuild"]`
11+
- [x] Added phantom deps as explicit devDependencies: `@sentry/core`, `@clack/prompts`
12+
- [x] Generated `pnpm-lock.yaml`
13+
- [x] Verified all patches apply (including cross-version: `@stricli/core@1.2.5` patch on `1.2.7`)
14+
15+
### Phase 2, Group D: SQLite Adapter
16+
- [x] Created `src/lib/db/sqlite.ts` — runtime-detecting adapter (bun:sqlite under Bun, node:sqlite under Node.js)
17+
- [x] Updated 4 source files: `db/index.ts`, `schema.ts`, `migration.ts`, `utils.ts`
18+
- [x] Updated 3 test files: `fix.test.ts`, `telemetry.test.ts`, `schema.test.ts`
19+
- [x] Zero `bun:sqlite` imports remain in `src/` or `test/`
20+
21+
## Remaining
22+
23+
### Phase 2: Source Code Migration (replace Bun.* APIs in `src/`)
24+
25+
**Group A: File I/O** — Replace `Bun.file()` / `Bun.write()` with `node:fs/promises`
26+
- `Bun.file(path).text()``readFile(path, "utf-8")`
27+
- `Bun.file(path).json()``readFile(path, "utf-8")` then `JSON.parse()`
28+
- `Bun.file(path).exists()``access(path).then(() => true, () => false)`
29+
- `Bun.write(path, content)``writeFile(path, content)`
30+
- Scan all of `src/` for occurrences
31+
32+
**Group B: Process/System APIs** — Replace Bun.which / Bun.spawn / Bun.sleep
33+
- `Bun.which("cmd")``which` from a Node.js-compatible package or custom implementation
34+
- `Bun.spawn()` / `Bun.spawnSync()``child_process.spawn()` / `spawnSync()`
35+
- `Bun.sleep(ms)``setTimeout` promise wrapper
36+
37+
**Group C: Miscellaneous Bun APIs**
38+
- `Bun.Glob``tinyglobby` or `picomatch` (already in devDependencies)
39+
- `Bun.randomUUIDv7()``uuidv7` package (already in devDependencies)
40+
- `Bun.semver.order()``semver.compare()` (already in devDependencies)
41+
- `Bun.zstdCompressSync()` / `Bun.zstdDecompressSync()` → Node.js zlib or `zstd-napi` package
42+
43+
**Group E: Unpolyfilled APIs**
44+
- `bspatch.ts` and `upgrade.ts` — Replace any Bun-specific APIs not covered by node-polyfills.ts
45+
46+
### Phase 3: Test Migration (`bun:test` → Vitest)
47+
48+
- Add `vitest` as devDependency
49+
- Replace `import { ... } from "bun:test"` with Vitest equivalents
50+
- Replace `bun test` scripts with `vitest`
51+
- Key differences:
52+
- `bun:test`'s `mock.module()` → Vitest's `vi.mock()`
53+
- `bun:test`'s `spyOn` → Vitest's `vi.spyOn()`
54+
- Test file discovery patterns may differ
55+
- `--isolate --parallel` behavior needs Vitest equivalent
56+
57+
### Phase 4: CI & Dev Scripts (remaining)
58+
59+
- Update `package.json` scripts: `bun run``pnpm run` where appropriate
60+
- Replace `bun run src/bin.ts` with `tsx src/bin.ts` (add `tsx` devDependency)
61+
- Replace `bun run script/*.ts` with `tsx script/*.ts`
62+
- Replace `bunx` with `pnpm exec`
63+
- Update GitHub Actions workflows to use pnpm + Node.js instead of Bun
64+
- Update `Dockerfile` / build scripts if applicable
65+
66+
### Phase 5: Cleanup
67+
68+
- Remove `@types/bun` from devDependencies
69+
- Remove `bun.lock` (replaced by `pnpm-lock.yaml`)
70+
- Remove or update `script/node-polyfills.ts` (may become unnecessary)
71+
- Update `AGENTS.md` Bun API reference table
72+
- Remove Bun-specific `.cursor/rules/bun-cli.mdc` or update for Node.js
73+
- Clean up any remaining `Bun.*` references in comments/docs
74+
75+
## Known Issues
76+
77+
- `test/lib/index.test.ts``sdk.run throws when auth is required but missing` fails under pnpm's strict `node_modules`. The mock fetch returns empty 200s which prevents the expected auth error from being thrown. Pre-existing test fragility, not caused by migration changes.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ Credentials are stored in `~/.sentry/` with restricted permissions (mode 600).
7777
## Library Usage
7878

7979
<!-- GENERATED:START library-prereq -->
80-
Use Sentry CLI programmatically in Node.js (≥22.12) or Bun without spawning a subprocess:
80+
Use Sentry CLI programmatically in Node.js (≥22.15) or Bun without spawning a subprocess:
8181
<!-- GENERATED:END library-prereq -->
8282

8383
```typescript

docs/src/content/docs/contributing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ cli/
5252
│ ├── context.ts # Dependency injection context
5353
│ ├── commands/ # CLI commands
5454
│ │ ├── auth/ # login, logout, refresh, status, token, whoami
55-
│ │ ├── cli/ # defaults, feedback, fix, setup, upgrade
55+
│ │ ├── cli/ # defaults, feedback, fix, import, setup, upgrade
5656
│ │ ├── dashboard/ # list, view, create, add, edit, delete, revisions, restore
5757
│ │ ├── event/ # view, list
5858
│ │ ├── issue/ # list, events, explain, plan, view, resolve, unresolve, archive, merge

lint-rules/no-manual-transactions.grit

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
file($name, $body) where {
22
$name <: not r".*migration\.ts$",
33
$name <: not r".*node-polyfills\.ts$",
4+
$name <: not r".*db/sqlite\.ts$",
45
$body <: contains `$db.exec($sql)` as $match where {
56
$sql <: or {
67
r".*BEGIN.*",

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
},
6767
"description": "Sentry CLI - A command-line interface for using Sentry built by robots and humans for robots and humans",
6868
"engines": {
69-
"node": ">=22.12"
69+
"node": ">=22.15"
7070
},
7171
"files": [
7272
"dist/bin.cjs",
@@ -115,6 +115,7 @@
115115
"check:fragments": "bun run script/check-fragments.ts",
116116
"check:deps": "bun run script/check-no-deps.ts",
117117
"check:errors": "bun run script/check-error-patterns.ts",
118+
"check:patches": "bun run script/check-patches.ts",
118119
"check:docs-sections": "bun run script/generate-docs-sections.ts --check"
119120
},
120121
"type": "module",

plugins/sentry-cli/skills/sentry-cli/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ CLI-related commands
345345
- `sentry cli defaults <key value...>` — View and manage default settings
346346
- `sentry cli feedback <message...>` — Send feedback about the CLI
347347
- `sentry cli fix` — Diagnose and repair CLI database issues
348+
- `sentry cli import` — Import settings from legacy .sentryclirc files
348349
- `sentry cli setup` — Configure shell integration
349350
- `sentry cli upgrade <version>` — Update the Sentry CLI to the latest version
350351

plugins/sentry-cli/skills/sentry-cli/references/cli.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ Diagnose and repair CLI database issues
4747
sentry cli fix
4848
```
4949

50+
### `sentry cli import`
51+
52+
Import settings from legacy .sentryclirc files
53+
54+
**Flags:**
55+
- `-y, --yes - Skip confirmation prompt`
56+
- `-n, --dry-run - Show what would happen without making changes`
57+
- `--url <value> - Explicitly trust this URL (bypasses same-file trust check)`
58+
- `--skip-validation - Skip token validation against the Sentry API`
59+
5060
### `sentry cli setup`
5161

5262
Configure shell integration

0 commit comments

Comments
 (0)