@sentry/junior-github 0.199.0 → 0.200.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/dist/index.js CHANGED
@@ -1001,7 +1001,9 @@ var createPullRequestToolInputSchema = z4.object({
1001
1001
  title: z4.string().describe("Pull request title."),
1002
1002
  head: z4.string().describe("Head branch or owner:branch ref."),
1003
1003
  base: z4.string().describe("Base branch."),
1004
- body: z4.string().describe("Pull request body. The runtime appends the conversation footer.").optional(),
1004
+ body: z4.string().describe(
1005
+ "Pull request body. The runtime appends the conversation footer."
1006
+ ).optional(),
1005
1007
  draft: z4.boolean().describe("Whether to open the pull request as a draft.").optional()
1006
1008
  }).strict();
1007
1009
  var createPullRequestStateSchema = Type2.Union([
@@ -1252,6 +1254,7 @@ function createGitHubPullRequestTool(ctx, subscriptionConfig) {
1252
1254
  readOnlyHint: false
1253
1255
  },
1254
1256
  description: "Create a GitHub pull request with a runtime-owned conversation footer. Use this instead of shelling out to gh pr create when creating pull requests.",
1257
+ exposure: "direct",
1255
1258
  inputSchema: createPullRequestToolInputSchema,
1256
1259
  outputSchema: gitHubPullRequestOutputSchema,
1257
1260
  async execute(input, options) {
@@ -2191,6 +2194,129 @@ function createGitHubResolvePullRequestReviewThreadTool(ctx, botEmail) {
2191
2194
  });
2192
2195
  }
2193
2196
 
2197
+ // src/tools/submit-pull-request-review.ts
2198
+ import {
2199
+ definePluginTool as definePluginTool11,
2200
+ PluginToolInputError as PluginToolInputError12,
2201
+ pluginToolOutputSchema as pluginToolOutputSchema11
2202
+ } from "@sentry/junior-plugin-api";
2203
+ import { z as z11 } from "zod";
2204
+ var reviewCommentSchema = z11.object({
2205
+ path: z11.string().trim().min(1).describe("File path in the pull request."),
2206
+ body: z11.string().trim().min(1).describe("Inline review comment."),
2207
+ line: z11.number().int().positive().describe("Line in the pull request diff."),
2208
+ side: z11.enum(["LEFT", "RIGHT"]).describe("Side of the pull request diff."),
2209
+ startLine: z11.number().int().positive().optional().describe("First line of a multi-line comment."),
2210
+ startSide: z11.enum(["LEFT", "RIGHT"]).optional().describe("First side of a multi-line comment.")
2211
+ }).strict().refine(
2212
+ ({ startLine, startSide }) => startLine === void 0 === (startSide === void 0),
2213
+ { message: "startLine and startSide must be provided together." }
2214
+ );
2215
+ var inputSchema9 = z11.object({
2216
+ repo: z11.string().describe('Repository in "owner/name" format.'),
2217
+ number: z11.number().int().positive().describe("Pull request number."),
2218
+ event: z11.enum(["COMMENT", "REQUEST_CHANGES"]).describe("Review result. Junior cannot approve pull requests."),
2219
+ body: z11.string().trim().min(1).describe("Review summary."),
2220
+ comments: z11.array(reviewCommentSchema).optional().describe("Optional inline review comments.")
2221
+ }).strict();
2222
+ var outputSchema9 = pluginToolOutputSchema11.extend({
2223
+ target: z11.literal("submitPullRequestReview"),
2224
+ repo: z11.string(),
2225
+ number: z11.number(),
2226
+ reviewId: z11.number(),
2227
+ state: z11.string(),
2228
+ url: z11.string()
2229
+ });
2230
+ function parseRepo11(value) {
2231
+ const parts = value.split("/").map((part) => part.trim());
2232
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
2233
+ throw new PluginToolInputError12('repo must use "owner/name" format');
2234
+ }
2235
+ return { owner: parts[0], name: parts[1], ref: `${parts[0]}/${parts[1]}` };
2236
+ }
2237
+ async function readJson8(response) {
2238
+ const text2 = await response.text();
2239
+ if (!text2) return void 0;
2240
+ try {
2241
+ return JSON.parse(text2);
2242
+ } catch {
2243
+ return text2;
2244
+ }
2245
+ }
2246
+ function githubError2(body) {
2247
+ if (body && typeof body === "object" && !Array.isArray(body)) {
2248
+ const message = body.message;
2249
+ if (typeof message === "string" && message.trim()) return message.trim();
2250
+ }
2251
+ if (typeof body === "string" && body.trim()) return body.trim();
2252
+ return "GitHub request failed";
2253
+ }
2254
+ function throwReviewError(status, body) {
2255
+ const message = `GitHub pull request review failed with HTTP ${status}: ${githubError2(body)}`;
2256
+ if (status === 404 || status === 422) {
2257
+ throw new PluginToolInputError12(message);
2258
+ }
2259
+ throw new Error(message);
2260
+ }
2261
+ function createGitHubSubmitPullRequestReviewTool(egress) {
2262
+ return definePluginTool11({
2263
+ annotations: {
2264
+ destructiveHint: true,
2265
+ idempotentHint: false,
2266
+ openWorldHint: true,
2267
+ readOnlyHint: false
2268
+ },
2269
+ description: "Submit a GitHub pull request review as a comment or change request. Use this instead of `gh pr review`, GraphQL, or direct REST calls. Junior cannot approve pull requests.",
2270
+ exposure: "direct",
2271
+ inputSchema: inputSchema9,
2272
+ outputSchema: outputSchema9,
2273
+ async execute(review) {
2274
+ const repo = parseRepo11(review.repo);
2275
+ const comments = review.comments?.map((comment) => ({
2276
+ path: comment.path,
2277
+ body: comment.body,
2278
+ line: comment.line,
2279
+ side: comment.side,
2280
+ ...comment.startLine !== void 0 ? { start_line: comment.startLine } : void 0,
2281
+ ...comment.startSide !== void 0 ? { start_side: comment.startSide } : void 0
2282
+ }));
2283
+ const response = await egress.fetch({
2284
+ provider: "github",
2285
+ operation: "github.pull.review.create",
2286
+ request: new Request(
2287
+ `https://api.github.com/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.name)}/pulls/${review.number}/reviews`,
2288
+ {
2289
+ method: "POST",
2290
+ headers: {
2291
+ Accept: "application/vnd.github+json",
2292
+ "Content-Type": "application/json",
2293
+ "X-GitHub-Api-Version": "2022-11-28"
2294
+ },
2295
+ body: JSON.stringify({
2296
+ body: review.body,
2297
+ event: review.event,
2298
+ ...comments ? { comments } : void 0
2299
+ })
2300
+ }
2301
+ )
2302
+ });
2303
+ const parsed = await readJson8(response);
2304
+ if (!response.ok) {
2305
+ throwReviewError(response.status, parsed);
2306
+ }
2307
+ const providerResult = z11.object({ id: z11.number(), html_url: z11.string(), state: z11.string() }).parse(parsed);
2308
+ return {
2309
+ target: "submitPullRequestReview",
2310
+ repo: repo.ref,
2311
+ number: review.number,
2312
+ reviewId: providerResult.id,
2313
+ state: providerResult.state,
2314
+ url: providerResult.html_url
2315
+ };
2316
+ }
2317
+ });
2318
+ }
2319
+
2194
2320
  // src/tools.ts
2195
2321
  function createGitHubTools(ctx, botEmail, pullRequestSubscription) {
2196
2322
  return {
@@ -2205,6 +2331,9 @@ function createGitHubTools(ctx, botEmail, pullRequestSubscription) {
2205
2331
  getRelease: createGitHubGetReleaseTool(ctx),
2206
2332
  getRepository: createGitHubGetRepositoryTool(ctx),
2207
2333
  resolvePullRequestReviewThread: createGitHubResolvePullRequestReviewThreadTool(ctx, botEmail),
2334
+ submitPullRequestReview: createGitHubSubmitPullRequestReviewTool(
2335
+ ctx.egress
2336
+ ),
2208
2337
  updateIssue: createGitHubUpdateIssueTool(ctx),
2209
2338
  updatePullRequest: createGitHubUpdatePullRequestTool(ctx)
2210
2339
  };
@@ -2215,7 +2344,7 @@ import { createHmac, timingSafeEqual } from "crypto";
2215
2344
 
2216
2345
  // src/issue-outcomes/store.ts
2217
2346
  import { and, eq, lte, sql as sql2 } from "drizzle-orm";
2218
- import { z as z12 } from "zod";
2347
+ import { z as z13 } from "zod";
2219
2348
 
2220
2349
  // src/db/schema.ts
2221
2350
  import { sql } from "drizzle-orm";
@@ -2227,18 +2356,18 @@ import {
2227
2356
  text,
2228
2357
  timestamp
2229
2358
  } from "drizzle-orm/pg-core";
2230
- import { z as z11 } from "zod";
2231
- var githubPullRequestStateSchema = z11.enum([
2359
+ import { z as z12 } from "zod";
2360
+ var githubPullRequestStateSchema = z12.enum([
2232
2361
  "closed_unmerged",
2233
2362
  "merged",
2234
2363
  "open"
2235
2364
  ]);
2236
- var githubPullRequestCommitCompositionSchema = z11.enum([
2365
+ var githubPullRequestCommitCompositionSchema = z12.enum([
2237
2366
  "junior_only",
2238
2367
  "mixed"
2239
2368
  ]);
2240
- var githubIssueStateSchema = z11.enum(["closed", "open"]);
2241
- var githubIssueStateReasonSchema = z11.enum([
2369
+ var githubIssueStateSchema = z12.enum(["closed", "open"]);
2370
+ var githubIssueStateReasonSchema = z12.enum([
2242
2371
  "completed",
2243
2372
  "duplicate",
2244
2373
  "not_planned",
@@ -2308,21 +2437,21 @@ var juniorGitHubPullRequestIssues = pgTable(
2308
2437
  );
2309
2438
 
2310
2439
  // src/issue-outcomes/store.ts
2311
- var githubIssueOutcomeInputSchema = z12.object({
2312
- candidateOwned: z12.boolean(),
2313
- closedAt: z12.date().optional(),
2314
- issueId: z12.string().min(1),
2315
- number: z12.number().int().positive(),
2316
- openedAt: z12.date(),
2317
- repositoryFullName: z12.string().min(1),
2318
- repositoryId: z12.string().min(1),
2440
+ var githubIssueOutcomeInputSchema = z13.object({
2441
+ candidateOwned: z13.boolean(),
2442
+ closedAt: z13.date().optional(),
2443
+ issueId: z13.string().min(1),
2444
+ number: z13.number().int().positive(),
2445
+ openedAt: z13.date(),
2446
+ repositoryFullName: z13.string().min(1),
2447
+ repositoryId: z13.string().min(1),
2319
2448
  state: githubIssueStateSchema,
2320
2449
  stateReason: githubIssueStateReasonSchema.optional(),
2321
- updatedAt: z12.date()
2450
+ updatedAt: z13.date()
2322
2451
  }).strict();
2323
- var githubIssueConversationsInputSchema = z12.object({
2324
- conversationIds: z12.array(z12.string().min(1)).min(1),
2325
- issueId: z12.string().min(1)
2452
+ var githubIssueConversationsInputSchema = z13.object({
2453
+ conversationIds: z13.array(z13.string().min(1)).min(1),
2454
+ issueId: z13.string().min(1)
2326
2455
  }).strict();
2327
2456
  function projectionValues(input) {
2328
2457
  return {
@@ -2386,33 +2515,33 @@ async function recordGitHubIssueConversations(db, input) {
2386
2515
 
2387
2516
  // src/pull-request-outcomes/store.ts
2388
2517
  import { and as and2, eq as eq2, lte as lte2, ne, sql as sql3 } from "drizzle-orm";
2389
- import { z as z13 } from "zod";
2390
- var githubPullRequestOutcomeInputSchema = z13.object({
2391
- candidateOwned: z13.boolean(),
2392
- closedAt: z13.date().optional(),
2518
+ import { z as z14 } from "zod";
2519
+ var githubPullRequestOutcomeInputSchema = z14.object({
2520
+ candidateOwned: z14.boolean(),
2521
+ closedAt: z14.date().optional(),
2393
2522
  commitComposition: githubPullRequestCommitCompositionSchema.optional(),
2394
- mergedAt: z13.date().optional(),
2395
- number: z13.number().int().positive(),
2396
- openedAt: z13.date(),
2397
- pullRequestId: z13.string().min(1),
2398
- repositoryFullName: z13.string().min(1),
2399
- repositoryId: z13.string().min(1),
2523
+ mergedAt: z14.date().optional(),
2524
+ number: z14.number().int().positive(),
2525
+ openedAt: z14.date(),
2526
+ pullRequestId: z14.string().min(1),
2527
+ repositoryFullName: z14.string().min(1),
2528
+ repositoryId: z14.string().min(1),
2400
2529
  state: githubPullRequestStateSchema,
2401
- title: z13.string().min(1).optional(),
2402
- updatedAt: z13.date()
2530
+ title: z14.string().min(1).optional(),
2531
+ updatedAt: z14.date()
2403
2532
  }).strict();
2404
- var githubPullRequestConversationsInputSchema = z13.object({
2405
- conversationIds: z13.array(z13.string().min(1)).min(1),
2406
- pullRequestId: z13.string().min(1)
2533
+ var githubPullRequestConversationsInputSchema = z14.object({
2534
+ conversationIds: z14.array(z14.string().min(1)).min(1),
2535
+ pullRequestId: z14.string().min(1)
2407
2536
  }).strict();
2408
- var githubPullRequestLinkedIssuesInputSchema = z13.object({
2409
- linkedIssues: z13.array(
2410
- z13.object({
2411
- number: z13.number().int().positive(),
2412
- repositoryFullName: z13.string().min(1)
2537
+ var githubPullRequestLinkedIssuesInputSchema = z14.object({
2538
+ linkedIssues: z14.array(
2539
+ z14.object({
2540
+ number: z14.number().int().positive(),
2541
+ repositoryFullName: z14.string().min(1)
2413
2542
  }).strict()
2414
2543
  ).min(1),
2415
- pullRequestId: z13.string().min(1)
2544
+ pullRequestId: z14.string().min(1)
2416
2545
  }).strict();
2417
2546
  function projectionValues2(input) {
2418
2547
  return {
@@ -2577,39 +2706,39 @@ async function recordGitHubPullRequestLinkedIssues(db, input) {
2577
2706
  }
2578
2707
 
2579
2708
  // src/webhooks/issue-outcome.ts
2580
- import { z as z14 } from "zod";
2581
- var canonicalIssueOutcomeSchema = z14.object({
2582
- action: z14.enum(["opened", "closed", "reopened"]),
2583
- issue: z14.object({
2584
- body: z14.string().nullable().optional(),
2585
- closed_at: z14.string().nullable().optional(),
2586
- created_at: z14.string(),
2587
- id: z14.number().int().positive(),
2588
- number: z14.number().int().positive(),
2709
+ import { z as z15 } from "zod";
2710
+ var canonicalIssueOutcomeSchema = z15.object({
2711
+ action: z15.enum(["opened", "closed", "reopened"]),
2712
+ issue: z15.object({
2713
+ body: z15.string().nullable().optional(),
2714
+ closed_at: z15.string().nullable().optional(),
2715
+ created_at: z15.string(),
2716
+ id: z15.number().int().positive(),
2717
+ number: z15.number().int().positive(),
2589
2718
  state_reason: githubIssueStateReasonSchema.nullable().optional(),
2590
- updated_at: z14.string(),
2591
- user: z14.object({ login: z14.string().min(1) }).strict()
2719
+ updated_at: z15.string(),
2720
+ user: z15.object({ login: z15.string().min(1) }).strict()
2592
2721
  }).strict(),
2593
- repository: z14.object({
2594
- full_name: z14.string().min(1),
2595
- id: z14.number().int().positive()
2722
+ repository: z15.object({
2723
+ full_name: z15.string().min(1),
2724
+ id: z15.number().int().positive()
2596
2725
  }).strict()
2597
2726
  }).strict();
2598
- var issueOutcomeSchema = z14.object({
2599
- action: z14.enum(["opened", "closed", "reopened"]),
2600
- issue: z14.object({
2601
- body: z14.string().nullable().optional(),
2602
- closed_at: z14.string().nullable().optional(),
2603
- created_at: z14.string(),
2604
- id: z14.number().int().positive(),
2605
- number: z14.number().int().positive(),
2727
+ var issueOutcomeSchema = z15.object({
2728
+ action: z15.enum(["opened", "closed", "reopened"]),
2729
+ issue: z15.object({
2730
+ body: z15.string().nullable().optional(),
2731
+ closed_at: z15.string().nullable().optional(),
2732
+ created_at: z15.string(),
2733
+ id: z15.number().int().positive(),
2734
+ number: z15.number().int().positive(),
2606
2735
  state_reason: githubIssueStateReasonSchema.nullable().optional(),
2607
- updated_at: z14.string(),
2608
- user: z14.object({ login: z14.string().min(1) }).passthrough()
2736
+ updated_at: z15.string(),
2737
+ user: z15.object({ login: z15.string().min(1) }).passthrough()
2609
2738
  }).passthrough(),
2610
- repository: z14.object({
2611
- full_name: z14.string().min(1),
2612
- id: z14.number().int().positive()
2739
+ repository: z15.object({
2740
+ full_name: z15.string().min(1),
2741
+ id: z15.number().int().positive()
2613
2742
  }).passthrough()
2614
2743
  }).passthrough().transform(
2615
2744
  (provider) => canonicalIssueOutcomeSchema.parse({
@@ -2630,7 +2759,7 @@ var issueOutcomeSchema = z14.object({
2630
2759
  }
2631
2760
  })
2632
2761
  );
2633
- var issueLifecycleActionSchema = z14.object({ action: z14.string() }).passthrough();
2762
+ var issueLifecycleActionSchema = z15.object({ action: z15.string() }).passthrough();
2634
2763
  function timestamp2(value) {
2635
2764
  if (!value) return void 0;
2636
2765
  const parsed = new Date(value);
@@ -2676,21 +2805,21 @@ function normalizeGitHubIssueOutcome(args) {
2676
2805
  updatedAt
2677
2806
  };
2678
2807
  }
2679
- var canonicalIssueConversationSchema = z14.object({
2680
- issue: z14.object({
2681
- body: z14.string().nullable().optional(),
2682
- id: z14.number().int().positive(),
2683
- user: z14.object({ login: z14.string().min(1) }).strict()
2808
+ var canonicalIssueConversationSchema = z15.object({
2809
+ issue: z15.object({
2810
+ body: z15.string().nullable().optional(),
2811
+ id: z15.number().int().positive(),
2812
+ user: z15.object({ login: z15.string().min(1) }).strict()
2684
2813
  }).strict(),
2685
- sender: z14.object({ login: z14.string().min(1) }).strict().optional()
2814
+ sender: z15.object({ login: z15.string().min(1) }).strict().optional()
2686
2815
  }).strict();
2687
- var issueConversationSchema = z14.object({
2688
- issue: z14.object({
2689
- body: z14.string().nullable().optional(),
2690
- id: z14.number().int().positive(),
2691
- user: z14.object({ login: z14.string().min(1) }).passthrough()
2816
+ var issueConversationSchema = z15.object({
2817
+ issue: z15.object({
2818
+ body: z15.string().nullable().optional(),
2819
+ id: z15.number().int().positive(),
2820
+ user: z15.object({ login: z15.string().min(1) }).passthrough()
2692
2821
  }).passthrough(),
2693
- sender: z14.object({ login: z14.string().min(1) }).passthrough().optional()
2822
+ sender: z15.object({ login: z15.string().min(1) }).passthrough().optional()
2694
2823
  }).passthrough().transform(
2695
2824
  (provider) => canonicalIssueConversationSchema.parse({
2696
2825
  issue: {
@@ -2717,43 +2846,43 @@ function normalizeGitHubIssueConversations(args) {
2717
2846
  }
2718
2847
 
2719
2848
  // src/webhooks/pull-request-outcome.ts
2720
- import { z as z15 } from "zod";
2721
- var canonicalPullRequestOutcomeSchema = z15.object({
2722
- action: z15.enum(["opened", "closed", "reopened"]),
2723
- pull_request: z15.object({
2724
- body: z15.string().nullable().optional(),
2725
- closed_at: z15.string().nullable().optional(),
2726
- created_at: z15.string(),
2727
- id: z15.number().int().positive(),
2728
- merged: z15.boolean(),
2729
- merged_at: z15.string().nullable().optional(),
2730
- number: z15.number().int().positive(),
2731
- title: z15.string().min(1).optional(),
2732
- updated_at: z15.string(),
2733
- user: z15.object({ login: z15.string().min(1) }).strict()
2849
+ import { z as z16 } from "zod";
2850
+ var canonicalPullRequestOutcomeSchema = z16.object({
2851
+ action: z16.enum(["opened", "closed", "reopened"]),
2852
+ pull_request: z16.object({
2853
+ body: z16.string().nullable().optional(),
2854
+ closed_at: z16.string().nullable().optional(),
2855
+ created_at: z16.string(),
2856
+ id: z16.number().int().positive(),
2857
+ merged: z16.boolean(),
2858
+ merged_at: z16.string().nullable().optional(),
2859
+ number: z16.number().int().positive(),
2860
+ title: z16.string().min(1).optional(),
2861
+ updated_at: z16.string(),
2862
+ user: z16.object({ login: z16.string().min(1) }).strict()
2734
2863
  }).strict(),
2735
- repository: z15.object({
2736
- full_name: z15.string().min(1),
2737
- id: z15.number().int().positive()
2864
+ repository: z16.object({
2865
+ full_name: z16.string().min(1),
2866
+ id: z16.number().int().positive()
2738
2867
  }).strict()
2739
2868
  }).strict();
2740
- var pullRequestOutcomeSchema = z15.object({
2741
- action: z15.enum(["opened", "closed", "reopened"]),
2742
- pull_request: z15.object({
2743
- body: z15.string().nullable().optional(),
2744
- closed_at: z15.string().nullable().optional(),
2745
- created_at: z15.string(),
2746
- id: z15.number().int().positive(),
2747
- merged: z15.boolean(),
2748
- merged_at: z15.string().nullable().optional(),
2749
- number: z15.number().int().positive(),
2750
- title: z15.string().min(1).optional(),
2751
- updated_at: z15.string(),
2752
- user: z15.object({ login: z15.string().min(1) }).passthrough()
2869
+ var pullRequestOutcomeSchema = z16.object({
2870
+ action: z16.enum(["opened", "closed", "reopened"]),
2871
+ pull_request: z16.object({
2872
+ body: z16.string().nullable().optional(),
2873
+ closed_at: z16.string().nullable().optional(),
2874
+ created_at: z16.string(),
2875
+ id: z16.number().int().positive(),
2876
+ merged: z16.boolean(),
2877
+ merged_at: z16.string().nullable().optional(),
2878
+ number: z16.number().int().positive(),
2879
+ title: z16.string().min(1).optional(),
2880
+ updated_at: z16.string(),
2881
+ user: z16.object({ login: z16.string().min(1) }).passthrough()
2753
2882
  }).passthrough(),
2754
- repository: z15.object({
2755
- full_name: z15.string().min(1),
2756
- id: z15.number().int().positive()
2883
+ repository: z16.object({
2884
+ full_name: z16.string().min(1),
2885
+ id: z16.number().int().positive()
2757
2886
  }).passthrough()
2758
2887
  }).passthrough().transform(
2759
2888
  (provider) => canonicalPullRequestOutcomeSchema.parse({
@@ -2776,24 +2905,24 @@ var pullRequestOutcomeSchema = z15.object({
2776
2905
  }
2777
2906
  })
2778
2907
  );
2779
- var pullRequestLifecycleActionSchema = z15.object({ action: z15.string() }).passthrough();
2780
- var canonicalPullRequestConversationSchema = z15.object({
2781
- pull_request: z15.object({
2782
- body: z15.string().nullable().optional(),
2783
- id: z15.number().int().positive(),
2784
- user: z15.object({ login: z15.string().min(1) }).strict()
2908
+ var pullRequestLifecycleActionSchema = z16.object({ action: z16.string() }).passthrough();
2909
+ var canonicalPullRequestConversationSchema = z16.object({
2910
+ pull_request: z16.object({
2911
+ body: z16.string().nullable().optional(),
2912
+ id: z16.number().int().positive(),
2913
+ user: z16.object({ login: z16.string().min(1) }).strict()
2785
2914
  }).strict(),
2786
- repository: z15.object({ full_name: z15.string().min(1) }).strict(),
2787
- sender: z15.object({ login: z15.string().min(1) }).strict()
2915
+ repository: z16.object({ full_name: z16.string().min(1) }).strict(),
2916
+ sender: z16.object({ login: z16.string().min(1) }).strict()
2788
2917
  }).strict();
2789
- var pullRequestConversationSchema = z15.object({
2790
- pull_request: z15.object({
2791
- body: z15.string().nullable().optional(),
2792
- id: z15.number().int().positive(),
2793
- user: z15.object({ login: z15.string().min(1) }).passthrough()
2918
+ var pullRequestConversationSchema = z16.object({
2919
+ pull_request: z16.object({
2920
+ body: z16.string().nullable().optional(),
2921
+ id: z16.number().int().positive(),
2922
+ user: z16.object({ login: z16.string().min(1) }).passthrough()
2794
2923
  }).passthrough(),
2795
- repository: z15.object({ full_name: z15.string().min(1) }).passthrough(),
2796
- sender: z15.object({ login: z15.string().min(1) }).passthrough()
2924
+ repository: z16.object({ full_name: z16.string().min(1) }).passthrough(),
2925
+ sender: z16.object({ login: z16.string().min(1) }).passthrough()
2797
2926
  }).passthrough().transform(
2798
2927
  (provider) => canonicalPullRequestConversationSchema.parse({
2799
2928
  pull_request: {
@@ -3075,18 +3204,18 @@ function createGitHubWebhookRoute(args) {
3075
3204
  }
3076
3205
 
3077
3206
  // src/pull-request-outcomes/commit-composition.ts
3078
- import { z as z16 } from "zod";
3079
- var canonicalCommitSchema = z16.object({
3080
- authorEmail: z16.string().nullable(),
3081
- authorLogin: z16.string().nullable()
3207
+ import { z as z17 } from "zod";
3208
+ var canonicalCommitSchema = z17.object({
3209
+ authorEmail: z17.string().nullable(),
3210
+ authorLogin: z17.string().nullable()
3082
3211
  }).strict();
3083
- var providerCommitSchema = z16.object({
3084
- author: z16.object({ login: z16.string() }).passthrough().nullable(),
3085
- commit: z16.object({
3086
- author: z16.object({ email: z16.string() }).passthrough().nullable()
3212
+ var providerCommitSchema = z17.object({
3213
+ author: z17.object({ login: z17.string() }).passthrough().nullable(),
3214
+ commit: z17.object({
3215
+ author: z17.object({ email: z17.string() }).passthrough().nullable()
3087
3216
  }).passthrough()
3088
3217
  }).passthrough();
3089
- var commitPageSchema = z16.array(providerCommitSchema).transform(
3218
+ var commitPageSchema = z17.array(providerCommitSchema).transform(
3090
3219
  (commits) => commits.map(
3091
3220
  (commit) => canonicalCommitSchema.parse({
3092
3221
  authorEmail: commit.commit.author?.email ?? null,
@@ -3965,6 +4094,13 @@ var GITHUB_BODY_WRITES = [
3965
4094
  operation: "github.pull.create",
3966
4095
  restPath: /^\/repos\/[^/]+\/[^/]+\/pulls$/
3967
4096
  },
4097
+ {
4098
+ denialMessage: `GitHub pull request reviews must use the github_submitPullRequestReview tool. The tool posts to /repos/{owner}/{repo}/pulls/{number}/reviews. ${CREATE_TOOL_ROUTING_GUIDANCE}`,
4099
+ graphqlField: "addPullRequestReview",
4100
+ method: "POST",
4101
+ operation: "github.pull.review.create",
4102
+ restPath: /^\/repos\/[^/]+\/[^/]+\/pulls\/[^/]+\/reviews$/
4103
+ },
3968
4104
  {
3969
4105
  denialMessage: `GitHub issue updates must use the github_updateIssue tool so Junior can own requester attribution and the conversation footer. ${CREATE_TOOL_ROUTING_GUIDANCE}`,
3970
4106
  graphqlField: "updateIssue",
@@ -4087,7 +4223,7 @@ function requireRepositoryTarget(upstreamUrl) {
4087
4223
  return;
4088
4224
  }
4089
4225
  throw new EgressPolicyDenied2(
4090
- "GitHub write request does not identify a target repository."
4226
+ "GitHub write request has no repository in its URL. Use a /repos/{owner}/{repo}/... URL or a GitHub tool for the operation."
4091
4227
  );
4092
4228
  }
4093
4229
  async function githubGrantForEgress(ctx) {
@@ -4154,7 +4290,7 @@ async function githubGrantForEgress(ctx) {
4154
4290
  if (graphqlAccess) {
4155
4291
  if (graphqlAccess === "write") {
4156
4292
  throw new EgressPolicyDenied2(
4157
- "GitHub GraphQL mutations are not enabled for runtime credentials."
4293
+ "GitHub GraphQL mutations are not enabled for runtime credentials. Use the matching GitHub tool or a supported REST endpoint. For pull request reviews, use github_submitPullRequestReview. It posts to /repos/{owner}/{repo}/pulls/{number}/reviews."
4158
4294
  );
4159
4295
  }
4160
4296
  return grantForAccess(
@@ -4166,7 +4302,7 @@ async function githubGrantForEgress(ctx) {
4166
4302
  const access = HTTP_READ_METHODS.has(method) ? "read" : "write";
4167
4303
  if (access === "write") {
4168
4304
  throw new EgressPolicyDenied2(
4169
- "GitHub write request is not an explicitly allowed Junior operation."
4305
+ "Junior does not support this GitHub write request. Use the matching GitHub tool or a supported REST endpoint."
4170
4306
  );
4171
4307
  }
4172
4308
  return grantForAccess(access, "github.api-read", "installation-read");
@@ -0,0 +1,42 @@
1
+ import { type PluginEgress } from "@sentry/junior-plugin-api";
2
+ /** Submit a comment or change-request review through GitHub's REST API. */
3
+ export declare function createGitHubSubmitPullRequestReviewTool(egress: PluginEgress): import("@sentry/junior-plugin-api").PluginToolDefinition<{
4
+ repo: string;
5
+ number: number;
6
+ event: "COMMENT" | "REQUEST_CHANGES";
7
+ body: string;
8
+ comments?: {
9
+ path: string;
10
+ body: string;
11
+ line: number;
12
+ side: "LEFT" | "RIGHT";
13
+ startLine?: number | undefined;
14
+ startSide?: "LEFT" | "RIGHT" | undefined;
15
+ }[] | undefined;
16
+ }, {
17
+ [x: string]: unknown;
18
+ target: "submitPullRequestReview";
19
+ repo: string;
20
+ number: number;
21
+ reviewId: number;
22
+ state: string;
23
+ url: string;
24
+ truncated?: boolean | undefined;
25
+ continuation?: {
26
+ arguments: Record<string, unknown>;
27
+ reason?: string | undefined;
28
+ } | undefined;
29
+ }, {
30
+ [x: string]: unknown;
31
+ target: "submitPullRequestReview";
32
+ repo: string;
33
+ number: number;
34
+ reviewId: number;
35
+ state: string;
36
+ url: string;
37
+ truncated?: boolean | undefined;
38
+ continuation?: {
39
+ arguments: Record<string, unknown>;
40
+ reason?: string | undefined;
41
+ } | undefined;
42
+ }>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior-github",
3
- "version": "0.199.0",
3
+ "version": "0.200.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -31,7 +31,7 @@
31
31
  "@sinclair/typebox": "^0.34.49",
32
32
  "drizzle-orm": "^0.45.2",
33
33
  "zod": "^4.5.4",
34
- "@sentry/junior-plugin-api": "0.199.0"
34
+ "@sentry/junior-plugin-api": "0.200.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@oxlint/plugins": "1.79.0",
@@ -3,36 +3,37 @@ name: github-code
3
3
  description: Work with GitHub repositories, source code, branches, commits, pull requests, reviews, diffs, CI, and repository credentials. Use for implementation, source investigation, clone/fetch/branch workflows, PR creation or updates, review feedback, GitHub Actions checks, and repository permission failures. Prefer this skill for repository tasks even when they concern a Sentry product.
4
4
  ---
5
5
 
6
- # GitHub Code Operations
6
+ # GitHub code
7
7
 
8
8
  Use `git` and `gh` for repository work.
9
9
 
10
- | Action | Tool / command |
11
- | --- | --- |
12
- | Create PR | `github_createPullRequest` (not `gh pr create`) |
13
- | Update PR title/body/base/state | `github_updatePullRequest` (not raw PATCH / `gh pr edit`) |
14
- | Resolve review thread | `github_resolvePullRequestReviewThread` (not raw GraphQL) |
15
- | Clone missing repo | `github_cloneRepository`; on Workspace match error use `switchWorkspace` |
10
+ | Action | Tool / command |
11
+ | ------------------------------- | ------------------------------------------------------------------------ |
12
+ | Create PR | `github_createPullRequest` (not `gh pr create`) |
13
+ | Update PR title/body/base/state | `github_updatePullRequest` |
14
+ | Submit PR review | `github_submitPullRequestReview` |
15
+ | Resolve review thread | `github_resolvePullRequestReviewThread` |
16
+ | Clone missing repo | `github_cloneRepository`; on Workspace match error use `switchWorkspace` |
16
17
 
17
18
  ## Open when needed
18
19
 
19
- | Need | Read |
20
- | --- | --- |
21
- | Commands, permissions, allowlist | [references/api-surface.md](references/api-surface.md) |
22
- | Edit → verify → PR packaging | [references/workflow.md](references/workflow.md) |
23
- | Failed command or permission recovery | [references/troubleshooting-workarounds.md](references/troubleshooting-workarounds.md) |
20
+ | Need | Read |
21
+ | ----------------------------- | -------------------------------------------------------------------------------------- |
22
+ | Commands and permissions | [references/api-surface.md](references/api-surface.md) |
23
+ | Edit, verify, and open a PR | [references/workflow.md](references/workflow.md) |
24
+ | Command or permission failure | [references/troubleshooting-workarounds.md](references/troubleshooting-workarounds.md) |
24
25
 
25
26
  ## Always
26
27
 
27
- - Resolve repo from the request, then `github.repo`. Run `jr-rpc config get github.repo` standalone.
28
- - Keep `--repo owner/repo` explicit on `gh`; use `git -C PATH` for local repos.
29
- - Read applicable `AGENTS.md` before editing. Narrower repo/task instructions win.
30
- - Preserve unrelated work. Never force-push, delete refs, or do destructive merges.
31
- - Base conclusions on repository evidence. Do not claim a check ran unless it did.
32
- - Push the branch before creating a bot-owned PR. Runtime injects installation credentials; never ask for a user token for bot pushes.
33
- - Tool-routing denials need the named tool. Only upstream denials justify permission remediation.
34
- - Stop for ambiguous targets, missing access, destructive ops, or unresolved upstream permission failures.
35
- - Unless the user opts out, finish completed edits with a pushed branch and PR (draft by default).
36
- - Report to the user: repo, branch, PR URL/number, local check results, and anything not run.
37
-
38
- Do not install or repair the GitHub plugin runtime from this skill. The plugin manifest owns that.
28
+ - Use the requested repo. Otherwise read `github.repo` with standalone `jr-rpc config get github.repo`.
29
+ - Pass `--repo owner/repo` to `gh`. Use `git -C PATH` for local repos.
30
+ - Read each applicable `AGENTS.md` before edits.
31
+ - Keep unrelated work. Never force-push, delete refs, or make destructive merges.
32
+ - Use repository evidence. Report only checks you ran.
33
+ - Push before `github_createPullRequest`. Never ask for a token for bot pushes.
34
+ - When an error names a tool, use it. Change permissions only when GitHub rejected the request.
35
+ - Stop for an unclear target, missing access, destructive work, or a GitHub permission failure.
36
+ - Unless the user opts out, push completed work and open a draft PR.
37
+ - Report repo, branch, PR, checks, and skipped checks.
38
+
39
+ Do not install or repair the GitHub plugin from this skill.
@@ -1,56 +1,56 @@
1
- # GitHub API surface — code and pull requests
1
+ # GitHub commands and permissions
2
2
 
3
- PR create/update and review-thread resolve use the tools named in `SKILL.md`. Other supported mutations use allowlisted REST through `gh api`. Generic GraphQL-backed `gh pr` mutations are not supported.
3
+ Use the tools in `SKILL.md` to create or update a PR, submit a review, or resolve a review thread. Use supported REST endpoints for other writes. Do not use GraphQL mutations.
4
4
 
5
5
  ## Repo targeting
6
6
 
7
- When the user omits `owner/repo`, resolve with standalone `jr-rpc config get github.repo`, then pass `--repo owner/repo` on the next `gh`/`git` command. Explicit repo flags target the command; they are not a credential scope.
7
+ If the user omits `owner/repo`, run `jr-rpc config get github.repo`. Then pass `--repo owner/repo` to `gh`.
8
8
 
9
9
  ## Permissions
10
10
 
11
- | Capability | Commands |
12
- | --- | --- |
13
- | `github.actions.read` | `gh run list`, `gh run view`, `gh run watch`, `gh workflow list`, `gh workflow view` |
14
- | `github.actions.write` | `gh workflow run`, `gh run rerun`, `gh run cancel` |
15
- | `github.contents.read` | `gh repo clone`, `git fetch` |
16
- | `github.contents.write` | Git smart-HTTP `git push` only |
17
- | `github.workflows.write` | Workflow-file changes on push |
18
- | `github.pull-requests.read` | `gh pr view`, `gh pr list`, `gh pr diff`, `gh pr checks` |
19
- | `github.pull-requests.write` | Typed PR create and allowlisted REST PR lifecycle |
11
+ | Capability | Commands |
12
+ | ---------------------------- | ------------------------------------------------------------------------------------ |
13
+ | `github.actions.read` | `gh run list`, `gh run view`, `gh run watch`, `gh workflow list`, `gh workflow view` |
14
+ | `github.actions.write` | `gh workflow run`, `gh run rerun`, `gh run cancel` |
15
+ | `github.contents.read` | `gh repo clone`, `git fetch` |
16
+ | `github.contents.write` | Git smart-HTTP `git push` only |
17
+ | `github.workflows.write` | Workflow-file changes on push |
18
+ | `github.pull-requests.read` | `gh pr view`, `gh pr list`, `gh pr diff`, `gh pr checks` |
19
+ | `github.pull-requests.write` | GitHub tools and supported REST writes |
20
20
 
21
21
  ## Commands
22
22
 
23
- | Operation | Command |
24
- | --- | --- |
25
- | Clone (default shallow) | `gh repo clone owner/repo [DIR] -- --depth=1` |
26
- | Fetch bounded base | `git -C DIR fetch --depth=N origin BASE:refs/remotes/origin/BASE` |
27
- | Deepen base | `git -C DIR fetch --deepen=N origin BASE:refs/remotes/origin/BASE` |
28
- | Unshallow | `git -C DIR fetch --unshallow origin` |
29
- | Shallow check | `git -C DIR rev-parse --is-shallow-repository` |
30
- | Branch / status | `git -C DIR branch --show-current` / `git -C DIR status --short --branch` |
31
- | Log / diff vs base | `git -C DIR log origin/BASE..HEAD --oneline` / `git -C DIR diff origin/BASE...HEAD` |
32
- | Default branch | `gh repo view owner/repo --json defaultBranchRef --jq .defaultBranchRef.name` |
33
- | Create branch | `git -C DIR checkout -b BRANCH` |
34
- | Commit | `git -C DIR add -A && git -C DIR commit -m "message"` |
35
- | Push | `git -C DIR push -u origin BRANCH` |
36
- | Workflow dispatch | `gh workflow run WORKFLOW --repo owner/repo --ref REF [-f key=value]` |
37
- | Rerun / cancel | `gh run rerun RUN_ID -R owner/repo [--failed]` / `gh run cancel RUN_ID -R owner/repo` |
38
- | Ready for review | `gh api repos/owner/repo/pulls/NUMBER/ready_for_review --method POST` |
39
- | Request reviewers | `gh api repos/owner/repo/pulls/NUMBER/requested_reviewers --method POST --input reviewers.json` |
40
- | Submit review | `gh api repos/owner/repo/pulls/NUMBER/reviews --method POST --input review.json` |
41
- | Inline review comment | `gh api repos/owner/repo/pulls/NUMBER/comments --method POST --input comment.json` |
42
- | View PR / checks | `gh pr view NUMBER --repo owner/repo` / `gh pr checks NUMBER --repo owner/repo` |
43
- | Diff PR | `gh pr diff NUMBER --repo owner/repo` |
44
- | List runs | `gh run list -R owner/repo --workflow WORKFLOW` |
45
- | View / watch run | `gh run view RUN_ID -R owner/repo` / `gh run watch RUN_ID -R owner/repo --exit-status` |
23
+ | Operation | Command |
24
+ | ----------------------- | ----------------------------------------------------------------------------------------------- |
25
+ | Clone (default shallow) | `gh repo clone owner/repo [DIR] -- --depth=1` |
26
+ | Fetch bounded base | `git -C DIR fetch --depth=N origin BASE:refs/remotes/origin/BASE` |
27
+ | Deepen base | `git -C DIR fetch --deepen=N origin BASE:refs/remotes/origin/BASE` |
28
+ | Unshallow | `git -C DIR fetch --unshallow origin` |
29
+ | Shallow check | `git -C DIR rev-parse --is-shallow-repository` |
30
+ | Branch / status | `git -C DIR branch --show-current` / `git -C DIR status --short --branch` |
31
+ | Log / diff vs base | `git -C DIR log origin/BASE..HEAD --oneline` / `git -C DIR diff origin/BASE...HEAD` |
32
+ | Default branch | `gh repo view owner/repo --json defaultBranchRef --jq .defaultBranchRef.name` |
33
+ | Create branch | `git -C DIR checkout -b BRANCH` |
34
+ | Commit | `git -C DIR add -A && git -C DIR commit -m "message"` |
35
+ | Push | `git -C DIR push -u origin BRANCH` |
36
+ | Workflow dispatch | `gh workflow run WORKFLOW --repo owner/repo --ref REF [-f key=value]` |
37
+ | Rerun / cancel | `gh run rerun RUN_ID -R owner/repo [--failed]` / `gh run cancel RUN_ID -R owner/repo` |
38
+ | Ready for review | `gh api repos/owner/repo/pulls/NUMBER/ready_for_review --method POST` |
39
+ | Request reviewers | `gh api repos/owner/repo/pulls/NUMBER/requested_reviewers --method POST --input reviewers.json` |
40
+ | Submit review | `github_submitPullRequestReview` |
41
+ | Inline review comment | `gh api repos/owner/repo/pulls/NUMBER/comments --method POST --input comment.json` |
42
+ | View PR / checks | `gh pr view NUMBER --repo owner/repo` / `gh pr checks NUMBER --repo owner/repo` |
43
+ | Diff PR | `gh pr diff NUMBER --repo owner/repo` |
44
+ | List runs | `gh run list -R owner/repo --workflow WORKFLOW` |
45
+ | View / watch run | `gh run view RUN_ID -R owner/repo` / `gh run watch RUN_ID -R owner/repo --exit-status` |
46
46
 
47
47
  ## Notes
48
48
 
49
49
  - Prefer `--json` where available. Pass clone flags after `--`.
50
- - Local commit does not call GitHub. Push uses installation credentials (`contents.write`; workflow files also need `workflows.write`).
51
- - Before history-dependent git work, deepen shallow clones; never force-push around missing ancestry.
52
- - Push head and resolve default `base` before `github_createPullRequest`.
53
- - Denied: merge, forks, REST contents/Git database writes, repo admin, raw PR PATCH, raw GraphQL mutations.
54
- - Reviews and inline comments post as the App bot via `installation-write`.
50
+ - A local commit does not call GitHub. A push uses installation credentials. Workflow changes also need `workflows.write`.
51
+ - Deepen a shallow clone before work that needs old history. Do not force-push to work around missing history.
52
+ - Push `head` and read the default `base` before `github_createPullRequest`.
53
+ - Junior does not support merges, forks, repository administration, REST content or Git database writes, direct PR update or review writes, or GraphQL mutations.
54
+ - Reviews and inline comments use the App bot.
55
55
  - PR comments/labels/assignees use issue endpoints; load `github-issues` for those.
56
56
  - Embed local images with `publishImage` first (public URL). Do not use private Slack file links.
@@ -1,33 +1,32 @@
1
- # GitHub CLI troubleshooting — code and pull requests
1
+ # GitHub troubleshooting
2
2
 
3
- | Symptom | Likely cause | Fix |
4
- | --- | --- | --- |
5
- | `unknown command` from `gh` | Runtime `gh` missing or too old | Report GitHub plugin runtime dependency unavailable |
6
- | `unknown flag: --depth` on clone | Clone flags before `--` | `gh repo clone owner/repo -- --depth=1` |
7
- | Missing `--repo` | No explicit target | Resolve `github.repo`, pass `--repo owner/repo` |
8
- | Wrong repo authenticated | Stale default | Pass `--repo owner/repo` or update `github.repo` |
9
- | GraphQL: could not resolve repository | Bad slug or no access | Validate `owner/repo` and App install |
10
- | 401 Unauthorized | Credential rejected | Confirm target; distinguish user OAuth vs installation setup |
11
- | `junior-auth-required` `user-write` | Missing/stale user OAuth | Follow private OAuth prompt; never ask for pasted tokens |
12
- | `git push` 401/403 | Install scope, remote, or permissions | Verify remote/repo, retry once, then report install scope |
13
- | `permission_denied` `source: "upstream"` | GitHub 403 after inject | Not a local runtime block; use grant/account/SSO fields |
14
- | 403 without upstream `permission_denied` | Local policy denial | Read body; follow required-tool guidance |
15
- | `Token scopes: none` on `gh auth status` | Normal for App user tokens | Use App permissions / accepted-permissions headers |
16
- | `github_createPullRequest` 401/403 | Install/repo lacks write | Report install scope; do not fall back to user OAuth |
17
- | Create PR 422 on `head` | Branch not pushed | Push branch; retry with explicit head/base |
18
- | Create/update PR 422 on `base` | Base missing | Resolve default branch; retry |
19
- | 403 names `github_updatePullRequest` | Raw PR PATCH blocked | Use `github_updatePullRequest` |
20
- | GraphQL mutations not enabled | Raw resolve blocked | Use `github_resolvePullRequestReviewThread` (bot-authored PRs only) |
21
- | Missing blame/old history | Shallow clone | Deepen needed refs; `--unshallow` only if required |
22
- | Odd ancestry / rebase fails | Base ref missing locally | Fetch `BASE:refs/remotes/origin/BASE`, deepen, use `origin/BASE` |
23
- | Missing deps in tests | Not installed | Frozen/immutable install for the lockfile; do not rewrite lockfile |
24
- | Frozen install fails | Drift or registry | Report exact failure |
25
- | `dnf install gh failed` | Plugin bootstrap | Report runtime bootstrap failure; do not repair from skill |
3
+ | Problem | Action |
4
+ | ---------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
5
+ | `gh` reports an unknown command | Report that the GitHub plugin is unavailable. Do not install it. |
6
+ | Clone rejects `--depth` | Put clone flags after `--`: `gh repo clone owner/repo -- --depth=1`. |
7
+ | Repository is missing or wrong | Read `github.repo`, then pass `--repo owner/repo`. |
8
+ | GraphQL cannot find the repository | Check `owner/repo` and the GitHub App installation. |
9
+ | `junior-auth-required` with `user-write` | Follow the private OAuth prompt. Never ask for a pasted token. |
10
+ | `git push` returns 401 or 403 | Check the remote and repository. Retry once, then report the installation access failure. |
11
+ | `permission_denied` has `source: "upstream"` | GitHub rejected the request. Report the grant, account, or SSO details. |
12
+ | Other 403 response | Read the error and use the tool it names. |
13
+ | `gh auth status` shows no token scopes | This is normal for GitHub App user tokens. Check App permissions instead. |
14
+ | `github_createPullRequest` returns 401 or 403 | Report the installation access failure. Do not use user OAuth. |
15
+ | PR creation returns 422 for `head` | Push the branch, then retry with explicit `head` and `base`. |
16
+ | PR create or update returns 422 for `base` | Read the default branch, then retry. |
17
+ | 403 names `github_updatePullRequest` | Use `github_updatePullRequest`. |
18
+ | PR review reports blocked GraphQL mutations | Use `github_submitPullRequestReview`. |
19
+ | Review-thread resolution reports blocked GraphQL mutations | Use `github_resolvePullRequestReviewThread`. It works only on bot-authored PRs. |
20
+ | Blame or old history is missing | Deepen the needed refs. Unshallow only when required. |
21
+ | Rebase has missing ancestry | Fetch `BASE:refs/remotes/origin/BASE`, deepen it, and use `origin/BASE`. |
22
+ | Dependencies are missing | Run the frozen install. Do not change the lockfile. |
23
+ | Frozen install fails | Report the exact failure. |
24
+ | Plugin setup fails | Report the setup failure. Do not repair it from the skill. |
26
25
 
27
26
  ## Retry rules
28
27
 
29
- - Retry once for transient transport after verifying repo context.
30
- - Do not loop on repeated 401/403/404 validation errors.
31
- - `user-read`/`user-write` gaps → private App OAuth. `installation-*` failures → App permission/install/host setup only.
32
- - Prefer `permission_denied` structured fields over guessing.
33
- - Persistent permission problems: report remediation and stop.
28
+ - Retry a transport failure once after you check the repository.
29
+ - Do not repeat 401, 403, 404, or validation failures.
30
+ - A `user-read` or `user-write` failure needs private App OAuth.
31
+ - An `installation-*` failure needs an App permission or installation fix.
32
+ - Use structured `permission_denied` details when present.