@sentry/junior-sentry 0.135.0 → 0.136.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,34 +1,45 @@
1
1
  # @sentry/junior-sentry
2
2
 
3
- `@sentry/junior-sentry` adds Sentry investigation workflows and explicitly requested alert/monitor creation to Junior via per-user OAuth.
4
-
5
- Install it alongside `@sentry/junior`:
3
+ Sentry investigations (per-user OAuth) and signed issue webhooks (internal integration) for Junior.
6
4
 
7
5
  ```bash
8
6
  pnpm add @sentry/junior @sentry/junior-sentry
9
7
  ```
10
8
 
11
- Add the package name to the plugin set exported from `plugins.ts`:
12
-
13
9
  ```ts
14
10
  import { defineJuniorPlugins } from "@sentry/junior";
11
+ import { sentryPlugin } from "@sentry/junior-sentry";
15
12
 
16
- export const plugins = defineJuniorPlugins(["@sentry/junior-sentry"]);
13
+ export const plugins = defineJuniorPlugins([sentryPlugin()]);
17
14
  ```
18
15
 
19
- ## Sentry CLI Surface
16
+ ## User OAuth
17
+
18
+ Set `SENTRY_CLIENT_ID` and `SENTRY_CLIENT_SECRET` from a Sentry OAuth app whose redirect is:
19
+
20
+ ```text
21
+ <base-url>/api/oauth/callback/sentry
22
+ ```
20
23
 
21
- The plugin installs the npm `sentry` package as a runtime dependency and injects the current user's OAuth token as `SENTRY_AUTH_TOKEN` for Sentry skill commands. The OAuth grant supports alert, issue, project, team, member lookup, and release workflows. Existing connections must reconnect after upgrading to grant newly added scopes.
24
+ Junior injects the user's token as `SENTRY_AUTH_TOKEN` for skill/CLI commands. Reconnect after scope changes.
22
25
 
23
- As of 2026-07-13, `sentry@latest` is `0.38.0`. The verified command groups used by the bundled skill are:
26
+ Verified CLI surface (check live `sentry --help` before blocking):
24
27
 
25
28
  - `sentry issue list|events|explain|plan|view`
26
29
  - `sentry org list|view`
27
30
  - `sentry log list|view`
28
31
  - `sentry trace list|view|logs`
29
32
  - `sentry alert metrics list|view|create|edit|delete`
30
- - `sentry api <endpoint>` as a fallback when no first-class command covers the request
33
+ - `sentry api <endpoint>` fallback
34
+
35
+ ## Issue webhooks
36
+
37
+ Use a **Sentry internal integration** (not a public app install flow):
38
+
39
+ 1. Subscribe to the **issue** webhook resource.
40
+ 2. Point it at `https://<junior-host>/api/webhooks/sentry`.
41
+ 3. Set the org slug as `SENTRY_WEBHOOK_ORG` and the integration client secret as `SENTRY_WEBHOOK_SECRET`, then redeploy.
31
42
 
32
- The skill must verify live `sentry --help` output before declaring a Sentry data surface unavailable. Prefer singular command groups such as `sentry org list`; do not use stale forms such as `sentry organizations list`.
43
+ Junior verifies `Sentry-Hook-Signature`, rejects payloads outside `SENTRY_WEBHOOK_ORG`, and publishes `issue.created` for `org/project#issueId` and `org/project`. Create watches/event tasks before the issue arrives; unmatched deliveries are not replayed.
33
44
 
34
- Full setup guide: https://junior.sentry.dev/extend/sentry-plugin/
45
+ Full guide: https://junior.sentry.dev/extend/sentry-plugin/
@@ -0,0 +1,183 @@
1
+ // src/resource-events/issue.ts
2
+ var SENTRY_ISSUE_EVENTS = ["issue.created"];
3
+ function sentryIssueResource(input) {
4
+ return {
5
+ identifier: `${input.org}/${input.project}#${input.issueId}`,
6
+ label: `Sentry issue ${input.org}/${input.project}#${input.issueId}`,
7
+ namespace: "sentry"
8
+ };
9
+ }
10
+ function sentryProjectResource(input) {
11
+ return {
12
+ identifier: `${input.org}/${input.project}`,
13
+ label: `Sentry project ${input.org}/${input.project}`,
14
+ namespace: "sentry"
15
+ };
16
+ }
17
+
18
+ // src/webhooks/resource-events.ts
19
+ import { z } from "zod";
20
+ var issueWebhookSchema = z.object({
21
+ action: z.string(),
22
+ data: z.object({
23
+ issue: z.object({
24
+ culprit: z.string().optional(),
25
+ firstSeen: z.string().optional(),
26
+ id: z.string().min(1),
27
+ issueCategory: z.string().optional(),
28
+ issueType: z.string().optional(),
29
+ level: z.string().optional(),
30
+ priority: z.string().optional(),
31
+ project: z.object({ slug: z.string().min(1) }).passthrough(),
32
+ status: z.string().optional(),
33
+ substatus: z.string().optional().nullable(),
34
+ title: z.string().optional(),
35
+ url: z.string().url(),
36
+ web_url: z.string().url().optional()
37
+ }).passthrough()
38
+ }),
39
+ installation: z.object({ uuid: z.string().min(1) }).passthrough()
40
+ }).passthrough();
41
+ function organizationSlug(issueApiUrl) {
42
+ try {
43
+ const segments = new URL(issueApiUrl).pathname.split("/").filter(Boolean);
44
+ const organizationsIndex = segments.indexOf("organizations");
45
+ return organizationsIndex >= 0 ? segments[organizationsIndex + 1] : void 0;
46
+ } catch {
47
+ return void 0;
48
+ }
49
+ }
50
+ function providerTime(value) {
51
+ if (!value) return void 0;
52
+ if (/^\d+$/.test(value)) {
53
+ const seconds = Number(value);
54
+ return Number.isSafeInteger(seconds) ? seconds * 1e3 : void 0;
55
+ }
56
+ const parsed = Date.parse(value);
57
+ return Number.isFinite(parsed) ? parsed : void 0;
58
+ }
59
+ function issueText(issue) {
60
+ const parts = [
61
+ issue.title ? `Title: ${issue.title}` : void 0,
62
+ issue.culprit ? `Culprit: ${issue.culprit}` : void 0,
63
+ issue.level ? `Level: ${issue.level}` : void 0,
64
+ issue.priority ? `Priority: ${issue.priority}` : void 0,
65
+ issue.issueCategory ? `Category: ${issue.issueCategory}` : void 0,
66
+ issue.issueType ? `Type: ${issue.issueType}` : void 0,
67
+ issue.status ? `Status: ${issue.status}` : void 0,
68
+ issue.substatus ? `Substatus: ${issue.substatus}` : void 0,
69
+ issue.web_url ? `URL: ${issue.web_url}` : void 0
70
+ ].filter((part) => part !== void 0);
71
+ return parts.length > 0 ? parts.join("\n") : void 0;
72
+ }
73
+ function normalizeSentryResourceEvents(input) {
74
+ if (input.hookResource !== "issue") return [];
75
+ const parsed = issueWebhookSchema.safeParse(input.body);
76
+ if (!parsed.success || parsed.data.action !== "created") return [];
77
+ const issue = parsed.data.data.issue;
78
+ const org = organizationSlug(issue.url)?.toLowerCase();
79
+ if (!org || org !== input.webhookOrg.toLowerCase()) return [];
80
+ const issueResource = sentryIssueResource({
81
+ issueId: issue.id,
82
+ org,
83
+ project: issue.project.slug
84
+ });
85
+ const projectResource = sentryProjectResource({
86
+ org,
87
+ project: issue.project.slug
88
+ });
89
+ const eventType = "issue.created";
90
+ const occurredAtMs = providerTime(issue.firstSeen) ?? providerTime(input.hookTimestamp) ?? Date.now();
91
+ const untrustedText = issueText(issue);
92
+ const event = {
93
+ // Sentry assigns a new Request-ID to retries, so use resource identity for
94
+ // idempotency across repeated deliveries of the same lifecycle event.
95
+ eventKey: `sentry:${issueResource.identifier}:${eventType}`,
96
+ eventType,
97
+ occurredAtMs,
98
+ trustedSummary: `${issueResource.label} was created.`,
99
+ ...untrustedText ? { untrustedText } : {}
100
+ };
101
+ return [
102
+ { ...event, identifier: issueResource.identifier },
103
+ { ...event, identifier: projectResource.identifier }
104
+ ];
105
+ }
106
+
107
+ // src/webhooks/handler.ts
108
+ import { createHmac, timingSafeEqual } from "crypto";
109
+ function verifySentrySignature(body, signature, secret) {
110
+ if (!secret || !/^[0-9a-f]{64}$/i.test(signature)) return false;
111
+ const actual = Buffer.from(signature.toLowerCase());
112
+ const expected = Buffer.from(
113
+ createHmac("sha256", secret).update(body).digest("hex")
114
+ );
115
+ return actual.length === expected.length && timingSafeEqual(actual, expected);
116
+ }
117
+ function parseJson(body) {
118
+ try {
119
+ return JSON.parse(body);
120
+ } catch {
121
+ return void 0;
122
+ }
123
+ }
124
+ function createSentryWebhookRoute(args) {
125
+ return {
126
+ method: "POST",
127
+ path: "/api/webhooks/sentry",
128
+ async handler(request) {
129
+ const rawBody = await request.text();
130
+ const signature = request.headers.get("sentry-hook-signature") ?? "";
131
+ if (!verifySentrySignature(rawBody, signature, args.webhookSecret())) {
132
+ return new Response("Unauthorized", { status: 401 });
133
+ }
134
+ const body = parseJson(rawBody);
135
+ if (body === void 0) {
136
+ return new Response("Malformed Sentry webhook", { status: 400 });
137
+ }
138
+ const requestId = request.headers.get("request-id")?.trim();
139
+ const hookResource = request.headers.get("sentry-hook-resource")?.trim();
140
+ if (!requestId || !hookResource) {
141
+ return new Response("Malformed Sentry webhook headers", {
142
+ status: 400
143
+ });
144
+ }
145
+ const webhookOrg = args.webhookOrg();
146
+ if (!webhookOrg) {
147
+ return new Response("Sentry webhook organization is not configured", {
148
+ status: 503
149
+ });
150
+ }
151
+ const events = normalizeSentryResourceEvents({
152
+ body,
153
+ hookResource,
154
+ hookTimestamp: request.headers.get("sentry-hook-timestamp")?.trim() || void 0,
155
+ webhookOrg
156
+ });
157
+ for (const event of events) {
158
+ await args.resourceEvents.publish(event);
159
+ }
160
+ return new Response(events.length ? "Accepted" : "Ignored", {
161
+ status: 202
162
+ });
163
+ }
164
+ };
165
+ }
166
+
167
+ // src/webhooks/secret.ts
168
+ function sentryWebhookSecret() {
169
+ return process.env.SENTRY_WEBHOOK_SECRET?.trim() || void 0;
170
+ }
171
+ function sentryWebhookOrg() {
172
+ return process.env.SENTRY_WEBHOOK_ORG?.trim().toLowerCase() || void 0;
173
+ }
174
+
175
+ export {
176
+ SENTRY_ISSUE_EVENTS,
177
+ sentryIssueResource,
178
+ sentryProjectResource,
179
+ normalizeSentryResourceEvents,
180
+ createSentryWebhookRoute,
181
+ sentryWebhookSecret,
182
+ sentryWebhookOrg
183
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Sentry plugin runtime boundary.
3
+ *
4
+ * This package owns per-user Sentry OAuth, CLI setup, internal-integration
5
+ * issue webhook normalization, and Sentry resource identities. Junior core owns
6
+ * watches and event tasks.
7
+ */
8
+ import { type PluginRegistration } from "@sentry/junior-plugin-api";
9
+ /** Register Sentry runtime metadata and signed resource-event ingress. */
10
+ export declare function sentryPlugin(): PluginRegistration;
package/dist/index.js ADDED
@@ -0,0 +1,81 @@
1
+ import {
2
+ SENTRY_ISSUE_EVENTS,
3
+ createSentryWebhookRoute,
4
+ sentryWebhookOrg,
5
+ sentryWebhookSecret
6
+ } from "./chunk-5IAUCX6U.js";
7
+
8
+ // src/index.ts
9
+ import {
10
+ defineJuniorPlugin
11
+ } from "@sentry/junior-plugin-api";
12
+ function sentryPlugin() {
13
+ return defineJuniorPlugin({
14
+ packageName: "@sentry/junior-sentry",
15
+ resourceEvents: {
16
+ resourceTypes: [
17
+ {
18
+ type: "issue",
19
+ supportedEvents: [...SENTRY_ISSUE_EVENTS],
20
+ suggestedEvents: [...SENTRY_ISSUE_EVENTS]
21
+ },
22
+ {
23
+ type: "project",
24
+ supportedEvents: [...SENTRY_ISSUE_EVENTS],
25
+ suggestedEvents: [...SENTRY_ISSUE_EVENTS]
26
+ }
27
+ ],
28
+ isEnabled: () => Boolean(sentryWebhookSecret()) && Boolean(sentryWebhookOrg()),
29
+ normalizeIdentifier: (identifier) => identifier.toLowerCase()
30
+ },
31
+ manifest: {
32
+ commandEnv: {
33
+ SENTRY_AUTH_TOKEN: "host_managed_credential"
34
+ },
35
+ configKeys: ["org", "project"],
36
+ credentials: {
37
+ authTokenEnv: "SENTRY_AUTH_TOKEN",
38
+ authTokenPlaceholder: "host_managed_credential",
39
+ domains: ["sentry.io", "us.sentry.io", "de.sentry.io"],
40
+ type: "oauth-bearer"
41
+ },
42
+ description: "Investigate Sentry telemetry, manage alerting, and receive issue webhooks",
43
+ displayName: "Sentry",
44
+ envVars: {
45
+ SENTRY_CLIENT_ID: {},
46
+ SENTRY_CLIENT_SECRET: {},
47
+ SENTRY_WEBHOOK_ORG: {},
48
+ SENTRY_WEBHOOK_SECRET: {}
49
+ },
50
+ name: "sentry",
51
+ oauth: {
52
+ authorizeEndpoint: "https://sentry.io/oauth/authorize/",
53
+ clientIdEnv: "SENTRY_CLIENT_ID",
54
+ clientSecretEnv: "SENTRY_CLIENT_SECRET",
55
+ scope: "alerts:write event:write member:read org:read project:releases project:write team:write",
56
+ tokenEndpoint: "https://sentry.io/oauth/token/"
57
+ },
58
+ runtimeDependencies: [
59
+ {
60
+ package: "sentry",
61
+ type: "npm",
62
+ version: "latest"
63
+ }
64
+ ]
65
+ },
66
+ hooks: {
67
+ routes(ctx) {
68
+ return [
69
+ createSentryWebhookRoute({
70
+ resourceEvents: ctx.resourceEvents,
71
+ webhookOrg: sentryWebhookOrg,
72
+ webhookSecret: sentryWebhookSecret
73
+ })
74
+ ];
75
+ }
76
+ }
77
+ });
78
+ }
79
+ export {
80
+ sentryPlugin
81
+ };
@@ -0,0 +1,13 @@
1
+ import type { SubscribableResource } from "@sentry/junior-plugin-api";
2
+ export declare const SENTRY_ISSUE_EVENTS: readonly ["issue.created"];
3
+ /** Build the stable Sentry issue identity shared by tools and webhooks. */
4
+ export declare function sentryIssueResource(input: {
5
+ issueId: string;
6
+ org: string;
7
+ project: string;
8
+ }): Pick<SubscribableResource, "identifier" | "label" | "namespace">;
9
+ /** Build the stable Sentry project identity used for project-scoped events. */
10
+ export declare function sentryProjectResource(input: {
11
+ org: string;
12
+ project: string;
13
+ }): Pick<SubscribableResource, "identifier" | "label" | "namespace">;
@@ -0,0 +1,4 @@
1
+ export { sentryIssueResource, sentryProjectResource, } from "./resource-events/issue.js";
2
+ export { createSentryWebhookRoute } from "./webhooks/handler.js";
3
+ export { normalizeSentryResourceEvents } from "./webhooks/resource-events.js";
4
+ export { sentryWebhookOrg, sentryWebhookSecret } from "./webhooks/secret.js";
@@ -0,0 +1,16 @@
1
+ import {
2
+ createSentryWebhookRoute,
3
+ normalizeSentryResourceEvents,
4
+ sentryIssueResource,
5
+ sentryProjectResource,
6
+ sentryWebhookOrg,
7
+ sentryWebhookSecret
8
+ } from "./chunk-5IAUCX6U.js";
9
+ export {
10
+ createSentryWebhookRoute,
11
+ normalizeSentryResourceEvents,
12
+ sentryIssueResource,
13
+ sentryProjectResource,
14
+ sentryWebhookOrg,
15
+ sentryWebhookSecret
16
+ };
@@ -0,0 +1,7 @@
1
+ import type { PluginRoute, ResourceEventPublisher } from "@sentry/junior-plugin-api";
2
+ /** Create the public, signed Sentry resource-event webhook route. */
3
+ export declare function createSentryWebhookRoute(args: {
4
+ resourceEvents: ResourceEventPublisher;
5
+ webhookOrg(): string | undefined;
6
+ webhookSecret(): string | undefined;
7
+ }): PluginRoute;
@@ -0,0 +1,8 @@
1
+ import type { ResourceEventInput } from "@sentry/junior-plugin-api";
2
+ /** Normalize one verified Sentry webhook into issue- and project-scoped events. */
3
+ export declare function normalizeSentryResourceEvents(input: {
4
+ body: unknown;
5
+ hookResource: string;
6
+ hookTimestamp?: string;
7
+ webhookOrg: string;
8
+ }): ResourceEventInput[];
@@ -0,0 +1,4 @@
1
+ /** Read the internal-integration client secret used for issue webhook ingress. */
2
+ export declare function sentryWebhookSecret(): string | undefined;
3
+ /** Read the Sentry organization allowed to publish issue webhooks. */
4
+ export declare function sentryWebhookOrg(): string | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior-sentry",
3
- "version": "0.135.0",
3
+ "version": "0.136.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -11,8 +11,35 @@
11
11
  "url": "git+https://github.com/getsentry/junior.git",
12
12
  "directory": "packages/junior-sentry"
13
13
  },
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "default": "./dist/index.js"
18
+ },
19
+ "./testing": {
20
+ "types": "./dist/testing.d.ts",
21
+ "default": "./dist/testing.js"
22
+ }
23
+ },
14
24
  "files": [
15
- "plugin.yaml",
25
+ "dist",
16
26
  "skills"
17
- ]
27
+ ],
28
+ "dependencies": {
29
+ "zod": "^4.4.3",
30
+ "@sentry/junior-plugin-api": "0.136.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^25.9.1",
34
+ "oxlint": "^1.66.0",
35
+ "tsup": "^8.5.1",
36
+ "typescript": "^6.0.3",
37
+ "vitest": "^4.1.7"
38
+ },
39
+ "scripts": {
40
+ "build": "tsup && tsc -p tsconfig.build.json --emitDeclarationOnly",
41
+ "lint": "oxlint --config ../junior/.oxlintrc.json --deny-warnings src tests tsup.config.ts vitest.config.ts",
42
+ "test": "vitest run",
43
+ "typecheck": "tsc --noEmit"
44
+ }
18
45
  }
@@ -4,22 +4,22 @@ Last updated: 2026-07-13
4
4
 
5
5
  ## Source inventory
6
6
 
7
- | Source | Trust tier | Confidence | Contribution | Usage constraints |
8
- | --------------------------------------------------------------- | ---------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
9
- | `https://github.com/getsentry/junior/issues/271` | canonical | high | Regression report: Junior tried stale `sentry organizations list` and should verify current CLI help before blocking. | Use as issue context, not as a full command reference. |
10
- | `https://cli.sentry.dev/commands/issue/` | canonical | high | Current `sentry issue list`, target syntax, issue subcommands, and JSON support. | Verify live help when runtime CLI differs. |
11
- | `https://cli.sentry.dev/commands/org/` | canonical | high | Current `sentry org list` and `sentry org view` commands. | Verify live help when runtime CLI differs. |
12
- | `https://cli.sentry.dev/commands/log/` | canonical | high | Current `sentry log list` and `sentry log view` commands, trace filtering, and log query flags. | Verify live help when runtime CLI differs. |
13
- | `https://cli.sentry.dev/commands/trace/` | canonical | high | Current `sentry trace list`, `view`, and `logs` commands. | Verify live help when runtime CLI differs. |
14
- | `https://cli.sentry.dev/commands/api/` | canonical | high | Authenticated `sentry api <endpoint>` fallback and request flags. | Use read-only requests unless the user asks for mutation. |
15
- | `https://cli.sentry.dev/configuration/` | canonical | high | `SENTRY_AUTH_TOKEN`, JSON/global flags, cache controls, and runtime configuration behavior. | Junior injects credentials; do not persist or print tokens. |
16
- | `pnpm view sentry version dist-tags description bin repository` | canonical | high | Confirmed npm package `sentry` latest is `0.38.0` and exposes the `sentry` binary. | Package metadata only; command behavior still comes from help/docs. |
17
- | `pnpm dlx sentry@latest --help` and subcommand help | canonical | high | Confirmed `alert metrics list|view|create|edit|delete`, including triggers and dry-run, plus the existing investigation commands. | Re-run when updating for a newer CLI. |
18
- | `packages/junior-sentry/plugin.yaml` | canonical | high | Confirms runtime dependency is the npm `sentry` package and auth token env is `SENTRY_AUTH_TOKEN`. | Local repo contract. |
19
- | `https://github.com/getsentry/junior/issues/615` | canonical | high | Regression report: Sentry product feature usage routed to Hex, then an explicit "use Sentry telemetry" redirect was ignored after Hex auth paused. | Use as routing evidence, not as command reference. |
20
- | `https://docs.sentry.io/api/monitors/create-a-monitor-for-a-project/` | canonical | high | Current public monitor creation endpoint, payload fields, and metric monitor examples. | Alerting API may evolve; verify live docs before writes. |
21
- | `https://docs.sentry.io/api/monitors/create-an-alert-for-an-organization/` | canonical | high | Current public alert workflow endpoint, connection fields, conditions, and actions. | Resolve integration and target IDs; never guess action identifiers. |
22
- | `getsentry/sentry` workflow engine endpoint and frontend form sources | canonical | high | Confirms `alerts:write`, `detectors`/`workflows` paths, dynamic anomaly payload shape, and legacy alert-rule deprecation. | Source-backed implementation detail; public API docs remain the user-facing contract. |
7
+ | Source | Trust tier | Confidence | Contribution | Usage constraints |
8
+ | -------------------------------------------------------------------------- | ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- | ------ | ---- | ---------------------------------------------------------------------------------- | ------------------------------------- |
9
+ | `https://github.com/getsentry/junior/issues/271` | canonical | high | Regression report: Junior tried stale `sentry organizations list` and should verify current CLI help before blocking. | Use as issue context, not as a full command reference. |
10
+ | `https://cli.sentry.dev/commands/issue/` | canonical | high | Current `sentry issue list`, target syntax, issue subcommands, and JSON support. | Verify live help when runtime CLI differs. |
11
+ | `https://cli.sentry.dev/commands/org/` | canonical | high | Current `sentry org list` and `sentry org view` commands. | Verify live help when runtime CLI differs. |
12
+ | `https://cli.sentry.dev/commands/log/` | canonical | high | Current `sentry log list` and `sentry log view` commands, trace filtering, and log query flags. | Verify live help when runtime CLI differs. |
13
+ | `https://cli.sentry.dev/commands/trace/` | canonical | high | Current `sentry trace list`, `view`, and `logs` commands. | Verify live help when runtime CLI differs. |
14
+ | `https://cli.sentry.dev/commands/api/` | canonical | high | Authenticated `sentry api <endpoint>` fallback and request flags. | Use read-only requests unless the user asks for mutation. |
15
+ | `https://cli.sentry.dev/configuration/` | canonical | high | `SENTRY_AUTH_TOKEN`, JSON/global flags, cache controls, and runtime configuration behavior. | Junior injects credentials; do not persist or print tokens. |
16
+ | `pnpm view sentry version dist-tags description bin repository` | canonical | high | Confirmed npm package `sentry` latest is `0.38.0` and exposes the `sentry` binary. | Package metadata only; command behavior still comes from help/docs. |
17
+ | `pnpm dlx sentry@latest --help` and subcommand help | canonical | high | Confirmed `alert metrics list | view | create | edit | delete`, including triggers and dry-run, plus the existing investigation commands. | Re-run when updating for a newer CLI. |
18
+ | `packages/junior-sentry/src/index.ts` | canonical | high | Confirms runtime dependency is the npm `sentry` package, auth token env is `SENTRY_AUTH_TOKEN`, and optional `SENTRY_WEBHOOK_SECRET` enables issue webhooks. | Local repo contract. |
19
+ | `https://github.com/getsentry/junior/issues/615` | canonical | high | Regression report: Sentry product feature usage routed to Hex, then an explicit "use Sentry telemetry" redirect was ignored after Hex auth paused. | Use as routing evidence, not as command reference. |
20
+ | `https://docs.sentry.io/api/monitors/create-a-monitor-for-a-project/` | canonical | high | Current public monitor creation endpoint, payload fields, and metric monitor examples. | Alerting API may evolve; verify live docs before writes. |
21
+ | `https://docs.sentry.io/api/monitors/create-an-alert-for-an-organization/` | canonical | high | Current public alert workflow endpoint, connection fields, conditions, and actions. | Resolve integration and target IDs; never guess action identifiers. |
22
+ | `getsentry/sentry` workflow engine endpoint and frontend form sources | canonical | high | Confirms `alerts:write`, `detectors`/`workflows` paths, dynamic anomaly payload shape, and legacy alert-rule deprecation. | Source-backed implementation detail; public API docs remain the user-facing contract. |
23
23
 
24
24
  ## Decisions
25
25
 
@@ -33,8 +33,8 @@ Last updated: 2026-07-13
33
33
  | Preserve stale plural subcommands as recommended forms. | rejected | `organizations list` was the root failure; aliases should not be taught as canonical command shapes. |
34
34
  | Create a broad new troubleshooting reference. | deferred | Current failure modes fit in the focused CLI reference without crowding `SKILL.md`. |
35
35
  | Permit explicitly requested alert/monitor writes only. | adopted | `alerts:write` is intentionally narrow; unrelated Sentry mutations remain out of scope. |
36
- | Prefer first-class `sentry alert metrics` commands. | adopted | CLI `0.38.0` supports list, view, create, edit, delete, triggers, and dry-run. |
37
- | Keep API fallback for dynamic anomaly detection. | adopted | The current CLI create flags expose threshold triggers but no dynamic/anomaly configuration. |
36
+ | Prefer first-class `sentry alert metrics` commands. | adopted | CLI `0.38.0` supports list, view, create, edit, delete, triggers, and dry-run. |
37
+ | Keep API fallback for dynamic anomaly detection. | adopted | The current CLI create flags expose threshold triggers but no dynamic/anomaly configuration. |
38
38
 
39
39
  ## Coverage matrix
40
40
 
@@ -50,7 +50,7 @@ Out of scope:
50
50
  Authoritative sources:
51
51
 
52
52
  - Current Sentry CLI docs and live `sentry --help` output.
53
- - The Sentry plugin manifest and Junior runtime contracts.
53
+ - The Sentry plugin runtime registration (`packages/junior-sentry/src/index.ts`) and Junior runtime contracts.
54
54
  - GitHub issues or PRs that document observed skill failures.
55
55
 
56
56
  Useful improvement sources:
package/plugin.yaml DELETED
@@ -1,27 +0,0 @@
1
- name: sentry
2
- display-name: Sentry
3
- description: Sentry issue tracking
4
-
5
- config-keys:
6
- - org
7
- - project
8
-
9
- credentials:
10
- type: oauth-bearer
11
- domains:
12
- - sentry.io
13
- - us.sentry.io
14
- - de.sentry.io
15
- auth-token-env: SENTRY_AUTH_TOKEN
16
- auth-token-placeholder: host_managed_credential
17
-
18
- oauth:
19
- client-id-env: SENTRY_CLIENT_ID
20
- client-secret-env: SENTRY_CLIENT_SECRET
21
- authorize-endpoint: https://sentry.io/oauth/authorize/
22
- token-endpoint: https://sentry.io/oauth/token/
23
- scope: "alerts:write event:write member:read org:read project:releases project:write team:write"
24
-
25
- runtime-dependencies:
26
- - type: npm
27
- package: "sentry"