@sentry/junior-github 0.239.0 → 0.240.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
|
@@ -2123,11 +2123,30 @@ var inputSchema7 = z10.object({
|
|
|
2123
2123
|
"Replacement pull request body. The runtime appends requester attribution and the conversation footer."
|
|
2124
2124
|
),
|
|
2125
2125
|
base: z10.string().trim().min(1).optional().describe("Replacement base branch."),
|
|
2126
|
-
state: z10.enum(["open", "closed"]).optional().describe("Replacement pull request state.")
|
|
2126
|
+
state: z10.enum(["open", "closed"]).optional().describe("Replacement pull request state."),
|
|
2127
|
+
draft: z10.boolean().optional().describe(
|
|
2128
|
+
"Set false to mark a draft pull request ready for review, or true to convert it back to a draft."
|
|
2129
|
+
)
|
|
2127
2130
|
}).strict().refine(
|
|
2128
|
-
({ title, body, base, state }) => title !== void 0 || body !== void 0 || base !== void 0 || state !== void 0,
|
|
2131
|
+
({ title, body, base, state, draft }) => title !== void 0 || body !== void 0 || base !== void 0 || state !== void 0 || draft !== void 0,
|
|
2129
2132
|
{ message: "At least one pull request field must be provided." }
|
|
2130
2133
|
);
|
|
2134
|
+
var MARK_READY_FOR_REVIEW = {
|
|
2135
|
+
operationName: "MarkPullRequestReadyForReview",
|
|
2136
|
+
query: "mutation MarkPullRequestReadyForReview($id: ID!) { result: markPullRequestReadyForReview(input: {pullRequestId: $id}) { pullRequest { isDraft } } }"
|
|
2137
|
+
};
|
|
2138
|
+
var CONVERT_TO_DRAFT = {
|
|
2139
|
+
operationName: "ConvertPullRequestToDraft",
|
|
2140
|
+
query: "mutation ConvertPullRequestToDraft($id: ID!) { result: convertPullRequestToDraft(input: {pullRequestId: $id}) { pullRequest { isDraft } } }"
|
|
2141
|
+
};
|
|
2142
|
+
var draftMutationResponseSchema = z10.object({
|
|
2143
|
+
data: z10.object({
|
|
2144
|
+
result: z10.object({ pullRequest: z10.object({ isDraft: z10.boolean() }) })
|
|
2145
|
+
})
|
|
2146
|
+
});
|
|
2147
|
+
var graphqlErrorsSchema = z10.object({
|
|
2148
|
+
errors: z10.array(z10.object({ message: z10.string() })).min(1)
|
|
2149
|
+
});
|
|
2131
2150
|
var pullRequestSchema2 = z10.object({
|
|
2132
2151
|
base: z10.string(),
|
|
2133
2152
|
body: z10.string().nullable(),
|
|
@@ -2173,6 +2192,34 @@ function githubApiErrorMessage4(payload) {
|
|
|
2173
2192
|
if (typeof payload === "string" && payload.trim()) return payload.trim();
|
|
2174
2193
|
return "GitHub request failed";
|
|
2175
2194
|
}
|
|
2195
|
+
async function setPullRequestDraft(input) {
|
|
2196
|
+
const mutation = input.draft ? CONVERT_TO_DRAFT : MARK_READY_FOR_REVIEW;
|
|
2197
|
+
const response = await input.egress.fetch({
|
|
2198
|
+
provider: "github",
|
|
2199
|
+
operation: `github.pull.draft.update:${input.repo.toLowerCase()}`,
|
|
2200
|
+
request: new Request("https://api.github.com/graphql", {
|
|
2201
|
+
method: "POST",
|
|
2202
|
+
headers: { "Content-Type": "application/json" },
|
|
2203
|
+
body: JSON.stringify({
|
|
2204
|
+
operationName: mutation.operationName,
|
|
2205
|
+
query: mutation.query,
|
|
2206
|
+
variables: { id: input.nodeId }
|
|
2207
|
+
})
|
|
2208
|
+
})
|
|
2209
|
+
});
|
|
2210
|
+
const payload = await readJson6(response);
|
|
2211
|
+
const isDraft = draftMutationResponseSchema.safeParse(payload).data?.data.result.pullRequest.isDraft;
|
|
2212
|
+
if (!response.ok || isDraft !== input.draft) {
|
|
2213
|
+
throw new Error(
|
|
2214
|
+
`GitHub pull request draft update failed with HTTP ${response.status}: ${githubGraphqlErrorMessage(payload)}`
|
|
2215
|
+
);
|
|
2216
|
+
}
|
|
2217
|
+
return isDraft;
|
|
2218
|
+
}
|
|
2219
|
+
function githubGraphqlErrorMessage(payload) {
|
|
2220
|
+
const result = graphqlErrorsSchema.safeParse(payload);
|
|
2221
|
+
return result.success ? result.data.errors.map((error) => error.message).join("; ") : githubApiErrorMessage4(payload);
|
|
2222
|
+
}
|
|
2176
2223
|
function createGitHubUpdatePullRequestTool(ctx) {
|
|
2177
2224
|
return definePluginTool9({
|
|
2178
2225
|
annotations: {
|
|
@@ -2181,7 +2228,7 @@ function createGitHubUpdatePullRequestTool(ctx) {
|
|
|
2181
2228
|
openWorldHint: true,
|
|
2182
2229
|
readOnlyHint: false
|
|
2183
2230
|
},
|
|
2184
|
-
description: "Update an existing GitHub pull request's title, body, base branch,
|
|
2231
|
+
description: "Update an existing GitHub pull request's title, body, base branch, open/closed state, or draft state. Use this instead of raw GitHub API calls when changing PR metadata or marking a PR ready for review.",
|
|
2185
2232
|
inputSchema: inputSchema7,
|
|
2186
2233
|
outputSchema: outputSchema7,
|
|
2187
2234
|
async execute(input) {
|
|
@@ -2206,21 +2253,20 @@ function createGitHubUpdatePullRequestTool(ctx) {
|
|
|
2206
2253
|
...update.base !== void 0 ? { base: update.base } : void 0,
|
|
2207
2254
|
...update.state !== void 0 ? { state: update.state } : void 0
|
|
2208
2255
|
};
|
|
2256
|
+
const pullUrl = `https://api.github.com/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.name)}/pulls/${update.number}`;
|
|
2257
|
+
const hasRestUpdate = Object.keys(payload).length > 0;
|
|
2209
2258
|
const response = await ctx.egress.fetch({
|
|
2210
2259
|
provider: "github",
|
|
2211
|
-
operation: "github.pull.update",
|
|
2212
|
-
request: new Request(
|
|
2213
|
-
|
|
2214
|
-
{
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
body: JSON.stringify(payload)
|
|
2222
|
-
}
|
|
2223
|
-
)
|
|
2260
|
+
operation: hasRestUpdate ? "github.pull.update" : "github.pull.get",
|
|
2261
|
+
request: new Request(pullUrl, {
|
|
2262
|
+
method: hasRestUpdate ? "PATCH" : "GET",
|
|
2263
|
+
headers: {
|
|
2264
|
+
Accept: "application/vnd.github+json",
|
|
2265
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
2266
|
+
...hasRestUpdate ? { "Content-Type": "application/json" } : void 0
|
|
2267
|
+
},
|
|
2268
|
+
...hasRestUpdate ? { body: JSON.stringify(payload) } : void 0
|
|
2269
|
+
})
|
|
2224
2270
|
});
|
|
2225
2271
|
const parsed = await readJson6(response);
|
|
2226
2272
|
if (!response.ok) {
|
|
@@ -2234,10 +2280,24 @@ function createGitHubUpdatePullRequestTool(ctx) {
|
|
|
2234
2280
|
draft: z10.boolean(),
|
|
2235
2281
|
html_url: z10.string(),
|
|
2236
2282
|
merged: z10.boolean(),
|
|
2283
|
+
node_id: z10.string().optional(),
|
|
2237
2284
|
number: z10.number(),
|
|
2238
2285
|
state: z10.string(),
|
|
2239
2286
|
title: z10.string()
|
|
2240
2287
|
}).parse(parsed);
|
|
2288
|
+
if (update.draft !== void 0 && update.draft !== providerResult.draft) {
|
|
2289
|
+
if (!providerResult.node_id) {
|
|
2290
|
+
throw new Error(
|
|
2291
|
+
"GitHub pull request response did not include node_id."
|
|
2292
|
+
);
|
|
2293
|
+
}
|
|
2294
|
+
providerResult.draft = await setPullRequestDraft({
|
|
2295
|
+
draft: update.draft,
|
|
2296
|
+
egress: ctx.egress,
|
|
2297
|
+
nodeId: providerResult.node_id,
|
|
2298
|
+
repo: repo.ref
|
|
2299
|
+
});
|
|
2300
|
+
}
|
|
2241
2301
|
const subscribable = ctx.events.canSubscribe ? gitHubPullRequestSubscribable({
|
|
2242
2302
|
number: providerResult.number,
|
|
2243
2303
|
repo: repo.ref
|
|
@@ -2255,7 +2315,10 @@ function createGitHubUpdatePullRequestTool(ctx) {
|
|
|
2255
2315
|
return {
|
|
2256
2316
|
objectAnnotations: [
|
|
2257
2317
|
githubObjectAnnotation({
|
|
2258
|
-
...githubObjectFacts("code_change",
|
|
2318
|
+
...githubObjectFacts("code_change", {
|
|
2319
|
+
...parsed,
|
|
2320
|
+
draft: providerResult.draft
|
|
2321
|
+
}),
|
|
2259
2322
|
repo: repo.ref,
|
|
2260
2323
|
number: providerResult.number,
|
|
2261
2324
|
title: providerResult.title,
|
|
@@ -4512,9 +4575,6 @@ function githubApiWriteGrantName(method, upstreamUrl) {
|
|
|
4512
4575
|
)) {
|
|
4513
4576
|
return "installation-write";
|
|
4514
4577
|
}
|
|
4515
|
-
if (method === "POST" && /^\/repos\/[^/]+\/[^/]+\/pulls\/[^/]+\/ready_for_review$/.test(pathname)) {
|
|
4516
|
-
return "installation-write";
|
|
4517
|
-
}
|
|
4518
4578
|
if ((method === "POST" || method === "DELETE") && /^\/repos\/[^/]+\/[^/]+\/pulls\/[^/]+\/requested_reviewers$/.test(pathname)) {
|
|
4519
4579
|
return "installation-write";
|
|
4520
4580
|
}
|
|
@@ -4536,18 +4596,36 @@ function githubApiWriteGrantName(method, upstreamUrl) {
|
|
|
4536
4596
|
}
|
|
4537
4597
|
return void 0;
|
|
4538
4598
|
}
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4599
|
+
var HOST_GRAPHQL_MUTATIONS = [
|
|
4600
|
+
{
|
|
4601
|
+
field: "resolveReviewThread",
|
|
4602
|
+
operationName: "ResolveReviewThread",
|
|
4603
|
+
operationPrefix: "github.pull.review-thread.resolve:"
|
|
4604
|
+
},
|
|
4605
|
+
{
|
|
4606
|
+
field: "markPullRequestReadyForReview",
|
|
4607
|
+
operationName: "MarkPullRequestReadyForReview",
|
|
4608
|
+
operationPrefix: "github.pull.draft.update:"
|
|
4609
|
+
},
|
|
4610
|
+
{
|
|
4611
|
+
field: "convertPullRequestToDraft",
|
|
4612
|
+
operationName: "ConvertPullRequestToDraft",
|
|
4613
|
+
operationPrefix: "github.pull.draft.update:"
|
|
4543
4614
|
}
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
if (parsed?.operationName !== "ResolveReviewThread" || !/\bmutation\s+ResolveReviewThread\b/.test(parsed.normalized) || !/\bresolveReviewThread\b/.test(parsed.normalized)) {
|
|
4615
|
+
];
|
|
4616
|
+
function hostGraphqlMutationRepository(operation, method, upstreamUrl, bodyText) {
|
|
4617
|
+
if (method !== "POST" || !isGitHubGraphqlUrl(upstreamUrl) || !operation) {
|
|
4548
4618
|
return void 0;
|
|
4549
4619
|
}
|
|
4550
|
-
|
|
4620
|
+
const parsed = parseGitHubGraphqlRequest(bodyText);
|
|
4621
|
+
const mutation = HOST_GRAPHQL_MUTATIONS.find(
|
|
4622
|
+
(candidate) => operation.startsWith(candidate.operationPrefix) && parsed?.operationName === candidate.operationName && new RegExp(`\\bmutation\\s+${candidate.operationName}\\b`).test(
|
|
4623
|
+
parsed.normalized
|
|
4624
|
+
) && new RegExp(`\\b${candidate.field}\\b`).test(parsed.normalized)
|
|
4625
|
+
);
|
|
4626
|
+
if (!mutation) return void 0;
|
|
4627
|
+
const repository = operation.slice(mutation.operationPrefix.length);
|
|
4628
|
+
return /^[^/]+\/[^/]+$/.test(repository) ? repository : void 0;
|
|
4551
4629
|
}
|
|
4552
4630
|
function isGitHubGraphqlMutation(method, upstreamUrl, bodyText, field) {
|
|
4553
4631
|
if (method !== "POST" || !isGitHubGraphqlUrl(upstreamUrl)) return false;
|
|
@@ -4650,13 +4728,13 @@ async function githubGrantForEgress(ctx) {
|
|
|
4650
4728
|
writeGrantName
|
|
4651
4729
|
);
|
|
4652
4730
|
}
|
|
4653
|
-
const
|
|
4731
|
+
const hostMutationRepository = hostGraphqlMutationRepository(
|
|
4654
4732
|
ctx.request.operation,
|
|
4655
4733
|
method,
|
|
4656
4734
|
upstreamUrl,
|
|
4657
4735
|
ctx.request.bodyText
|
|
4658
4736
|
);
|
|
4659
|
-
if (
|
|
4737
|
+
if (hostMutationRepository) {
|
|
4660
4738
|
return grantForAccess(
|
|
4661
4739
|
"write",
|
|
4662
4740
|
"github.installation-write",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior-github",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.240.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.
|
|
34
|
+
"@sentry/junior-plugin-api": "0.240.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@oxlint/plugins": "1.79.0",
|
|
@@ -11,6 +11,7 @@ Use `git` and `gh` for repository work.
|
|
|
11
11
|
| ------------------------------- | ------------------------------------------------------------------------ |
|
|
12
12
|
| Create PR | `github_createPullRequest` (not `gh pr create`) |
|
|
13
13
|
| Update PR title/body/base/state | `github_updatePullRequest` |
|
|
14
|
+
| Mark PR ready / draft | `github_updatePullRequest` with `draft: false` / `draft: true` |
|
|
14
15
|
| Submit PR review | `github_submitPullRequestReview` |
|
|
15
16
|
| Resolve review thread | `github_resolvePullRequestReviewThread` |
|
|
16
17
|
| Clone missing repo | `github_cloneRepository`; on Workspace match error use `switchWorkspace` |
|
|
@@ -33,7 +34,7 @@ Use `git` and `gh` for repository work.
|
|
|
33
34
|
- Push before `github_createPullRequest`. Never ask for a token for bot pushes.
|
|
34
35
|
- When an error names a tool, use it. Change permissions only when GitHub rejected the request.
|
|
35
36
|
- 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
|
|
37
|
+
- Unless the user opts out, push completed work and open a PR. Open it ready for review when the work is complete and the checks pass. Open a draft only when work, checks, or decisions remain.
|
|
37
38
|
- Report repo, branch, PR, checks, and skipped checks.
|
|
38
39
|
|
|
39
40
|
Do not install or repair the GitHub plugin from this skill.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# GitHub commands and permissions
|
|
2
2
|
|
|
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
|
|
3
|
+
Use the tools in `SKILL.md` to create or update a PR, mark it ready for review or draft, submit a review, or resolve a review thread. Use supported REST endpoints for other writes. Do not send GraphQL mutations, for example with `gh api graphql`. The egress proxy denies them.
|
|
4
4
|
|
|
5
5
|
## Repo targeting
|
|
6
6
|
|
|
@@ -35,7 +35,7 @@ If the user omits `owner/repo`, run `jr-rpc config get github.repo`. Then pass `
|
|
|
35
35
|
| Push | `git -C DIR push -u origin BRANCH` |
|
|
36
36
|
| Workflow dispatch | `gh workflow run WORKFLOW --repo owner/repo --ref REF [-f key=value]` |
|
|
37
37
|
| Rerun / cancel | `gh run rerun RUN_ID -R owner/repo [--failed]` / `gh run cancel RUN_ID -R owner/repo` |
|
|
38
|
-
|
|
|
38
|
+
| Mark PR ready / draft | `github_updatePullRequest` with `draft: false` / `draft: true` |
|
|
39
39
|
| Request reviewers | `gh api repos/owner/repo/pulls/NUMBER/requested_reviewers --method POST --input reviewers.json` |
|
|
40
40
|
| Submit review | `github_submitPullRequestReview` |
|
|
41
41
|
| Set feedback status | `github_updatePullRequestFeedback` |
|
|
@@ -52,7 +52,7 @@ If the user omits `owner/repo`, run `jr-rpc config get github.repo`. Then pass `
|
|
|
52
52
|
- A local commit does not call GitHub. A push uses installation credentials. Workflow changes also need `workflows.write`.
|
|
53
53
|
- Deepen a shallow clone before work that needs old history. Do not force-push to work around missing history.
|
|
54
54
|
- Push `head` and read the default `base` before `github_createPullRequest`.
|
|
55
|
-
- Junior does not support merges, forks, repository administration, REST content or Git database writes, direct PR update or review writes, or GraphQL mutations.
|
|
55
|
+
- Junior does not support merges, forks, repository administration, REST content or Git database writes, direct PR update or review writes, auto-merge, or GraphQL mutations.
|
|
56
56
|
- Reviews and inline comments use the App bot.
|
|
57
57
|
- PR comments/labels/assignees use issue endpoints; load `github-issues` for those.
|
|
58
58
|
- Embed local images with `publishImage` first (public URL). Do not use private Slack file links.
|
|
@@ -35,7 +35,7 @@ Open this when making repository edits, not for read-only inspection.
|
|
|
35
35
|
2. Commit with repo conventions, else `<type>(<scope>): <Subject>` imperative present, no agent branding.
|
|
36
36
|
3. Push with `git push -u origin BRANCH`.
|
|
37
37
|
4. Resolve the actual default branch.
|
|
38
|
-
5. Update an existing PR for the branch with `github_updatePullRequest`, or create with `github_createPullRequest` (`draft: true`
|
|
38
|
+
5. Update an existing PR for the branch with `github_updatePullRequest`, or create with `github_createPullRequest` (`draft: true` only when work, checks, or decisions remain; otherwise ready for review).
|
|
39
39
|
6. PR title matches the current dominant change, same conventional form as commits.
|
|
40
40
|
7. PR body: short, plain English, what changed and why. Only context the diff cannot show. No empty sections, file lists, commit logs, or Checks/Verification/Test plan blocks. Put local check results in the user report only.
|
|
41
41
|
8. After material follow-up commits, refresh title and body against the current diff.
|